LCOV - code coverage report
Current view: top level - flamenco/progcache - fd_progcache_admin.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 192 382 50.3 %
Date: 2026-01-23 05:02:40 Functions: 14 22 63.6 %

          Line data    Source code
       1             : #include "fd_progcache_admin.h"
       2             : #include "fd_progcache_rec.h"
       3             : #include "fd_prog_load.h"
       4             : #include "../runtime/program/fd_bpf_loader_program.h"
       5             : #include "../runtime/program/fd_loader_v4_program.h"
       6             : #include "../runtime/fd_system_ids.h"
       7             : #include "../../funk/fd_funk_rec.h"
       8             : 
       9             : /* Algorithm to estimate size of cache metadata structures (rec_pool
      10             :    object pool and rec_map hashchain table).
      11             : 
      12             :    FIXME Carefully balance this */
      13             : 
      14             : static ulong
      15             : fd_progcache_est_rec_max1( ulong wksp_footprint,
      16           0 :                            ulong mean_cache_entry_size ) {
      17           0 :   return wksp_footprint / mean_cache_entry_size;
      18           0 : }
      19             : 
      20             : ulong
      21             : fd_progcache_est_rec_max( ulong wksp_footprint,
      22           0 :                           ulong mean_cache_entry_size ) {
      23           0 :   ulong est = fd_progcache_est_rec_max1( wksp_footprint, mean_cache_entry_size );
      24           0 :   if( FD_UNLIKELY( est>(1UL<<31) ) ) FD_LOG_ERR(( "fd_progcache_est_rec_max(wksp_footprint=%lu,mean_cache_entry_size=%lu) failed: invalid parameters", wksp_footprint, mean_cache_entry_size ));
      25           0 :   return fd_ulong_max( est, 2048UL );
      26           0 : }
      27             : 
      28             : fd_progcache_admin_t *
      29             : fd_progcache_admin_join( fd_progcache_admin_t * ljoin,
      30          51 :                          void *                 shfunk ) {
      31          51 :   if( FD_UNLIKELY( !ljoin ) ) {
      32           0 :     FD_LOG_WARNING(( "NULL ljoin" ));
      33           0 :     return NULL;
      34           0 :   }
      35          51 :   if( FD_UNLIKELY( !shfunk ) ) {
      36           0 :     FD_LOG_WARNING(( "NULL shfunk" ));
      37           0 :     return NULL;
      38           0 :   }
      39             : 
      40          51 :   memset( ljoin, 0, sizeof(fd_progcache_admin_t) );
      41          51 :   if( FD_UNLIKELY( !fd_funk_join( ljoin->funk, shfunk ) ) ) {
      42           0 :     FD_LOG_CRIT(( "fd_funk_join failed" ));
      43           0 :   }
      44             : 
      45          51 :   return ljoin;
      46          51 : }
      47             : 
      48             : void *
      49             : fd_progcache_admin_leave( fd_progcache_admin_t * ljoin,
      50          48 :                           void **                opt_shfunk ) {
      51          48 :   if( FD_UNLIKELY( !ljoin ) ) FD_LOG_CRIT(( "NULL ljoin" ));
      52             : 
      53          48 :   if( FD_UNLIKELY( !fd_funk_leave( ljoin->funk, opt_shfunk ) ) ) FD_LOG_CRIT(( "fd_funk_leave failed" ));
      54             : 
      55          48 :   return ljoin;
      56          48 : }
      57             : 
      58             : /* Begin transaction-level operations.  It is assumed that funk_txn data
      59             :    structures are not concurrently modified.  This includes txn_pool and
      60             :    txn_map. */
      61             : 
      62             : void
      63             : fd_progcache_txn_attach_child( fd_progcache_admin_t *    cache,
      64             :                                fd_funk_txn_xid_t const * xid_parent,
      65         102 :                                fd_funk_txn_xid_t const * xid_new ) {
      66         102 :   FD_LOG_INFO(( "progcache txn laddr=%p xid %lu:%lu: created with parent %lu:%lu",
      67         102 :                 (void *)cache->funk,
      68         102 :                 xid_new   ->ul[0], xid_new   ->ul[1],
      69         102 :                 xid_parent->ul[0], xid_parent->ul[1] ));
      70         102 :   fd_funk_txn_prepare( cache->funk, xid_parent, xid_new );
      71         102 : }
      72             : 
      73             : static void
      74             : fd_progcache_txn_cancel_one( fd_progcache_admin_t * cache,
      75          66 :                              fd_funk_txn_t *        txn ) {
      76          66 :   FD_LOG_INFO(( "progcache txn laddr=%p xid %lu:%lu: cancel", (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
      77             : 
      78          66 :   fd_funk_t * funk = cache->funk;
      79          66 :   if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( txn->child_head_cidx ) ||
      80          66 :                    !fd_funk_txn_idx_is_null( txn->child_tail_cidx ) ) ) {
      81           0 :     FD_LOG_CRIT(( "fd_progcache_txn_cancel failed: txn at %p with xid %lu:%lu has children (data corruption?)",
      82           0 :                   (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
      83           0 :   }
      84             : 
      85             :   /* Phase 1: Drain users from transaction */
      86             : 
      87          66 :   fd_rwlock_write( txn->lock );
      88          66 :   FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_CANCEL;
      89             : 
      90             :   /* Phase 2: Remove records */
      91             : 
      92         117 :   while( !fd_funk_rec_idx_is_null( txn->rec_head_idx ) ) {
      93          51 :     fd_funk_rec_t * rec = &funk->rec_pool->ele[ txn->rec_head_idx ];
      94          51 :     uint next_idx = rec->next_idx;
      95          51 :     rec->next_idx = FD_FUNK_REC_IDX_NULL;
      96          51 :     if( FD_LIKELY( !fd_funk_rec_idx_is_null( next_idx ) ) ) {
      97           0 :       funk->rec_pool->ele[ next_idx ].prev_idx = FD_FUNK_REC_IDX_NULL;
      98           0 :     }
      99             : 
     100          51 :     fd_funk_val_flush( rec, funk->alloc, funk->wksp );
     101             : 
     102          51 :     fd_funk_rec_query_t query[1];
     103          51 :     int remove_err = fd_funk_rec_map_remove( funk->rec_map, &rec->pair, NULL, query, FD_MAP_FLAG_BLOCKING );
     104          51 :     if( FD_UNLIKELY( remove_err ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", remove_err, fd_map_strerror( remove_err ) ));
     105             : 
     106          51 :     fd_funk_rec_pool_release( funk->rec_pool, rec, 1 );
     107             : 
     108          51 :     txn->rec_head_idx = next_idx;
     109          51 :     if( fd_funk_rec_idx_is_null( next_idx ) ) txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
     110          51 :   }
     111             : 
     112             :   /* Phase 3: Remove transaction from fork graph */
     113             : 
     114          66 :   uint self_cidx = fd_funk_txn_cidx( (ulong)( txn-funk->txn_pool->ele ) );
     115          66 :   uint prev_cidx = txn->sibling_prev_cidx; ulong prev_idx = fd_funk_txn_idx( prev_cidx );
     116          66 :   uint next_cidx = txn->sibling_next_cidx; ulong next_idx = fd_funk_txn_idx( next_cidx );
     117          66 :   if( !fd_funk_txn_idx_is_null( next_idx ) ) {
     118           0 :     funk->txn_pool->ele[ next_idx ].sibling_prev_cidx = prev_cidx;
     119           0 :   }
     120          66 :   if( !fd_funk_txn_idx_is_null( prev_idx ) ) {
     121           0 :     funk->txn_pool->ele[ prev_idx ].sibling_next_cidx = next_cidx;
     122           0 :   }
     123          66 :   if( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) {
     124          30 :     fd_funk_txn_t * parent = &funk->txn_pool->ele[ fd_funk_txn_idx( txn->parent_cidx ) ];
     125          30 :     if( parent->child_head_cidx==self_cidx ) parent->child_head_cidx = next_cidx;
     126          30 :     if( parent->child_tail_cidx==self_cidx ) parent->child_tail_cidx = prev_cidx;
     127          36 :   } else {
     128          36 :     if( funk->shmem->child_head_cidx==self_cidx ) funk->shmem->child_head_cidx = next_cidx;
     129          36 :     if( funk->shmem->child_tail_cidx==self_cidx ) funk->shmem->child_tail_cidx = prev_cidx;
     130          36 :   }
     131             : 
     132             :   /* Phase 4: Remove transcation from index */
     133             : 
     134          66 :   fd_funk_txn_map_query_t query[1];
     135          66 :   int remove_err = fd_funk_txn_map_remove( funk->txn_map, &txn->xid, NULL, query, FD_MAP_FLAG_BLOCKING );
     136          66 :   if( FD_UNLIKELY( remove_err!=FD_MAP_SUCCESS ) ) {
     137           0 :     FD_LOG_CRIT(( "fd_progcache_txn_cancel failed: fd_funk_txn_map_remove(%lu:%lu) failed: %i-%s",
     138           0 :                   txn->xid.ul[0], txn->xid.ul[1], remove_err, fd_map_strerror( remove_err ) ));
     139           0 :   }
     140             : 
     141             :   /* Phase 5: Free transaction object */
     142             : 
     143          66 :   fd_rwlock_unwrite( txn->lock );
     144          66 :   FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_FREE;
     145          66 :   fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
     146          66 : }
     147             : 
     148             : /* Cancels txn and all children */
     149             : 
     150             : static void
     151             : fd_progcache_txn_cancel_tree( fd_progcache_admin_t * cache,
     152          66 :                               fd_funk_txn_t *        txn ) {
     153          84 :   for(;;) {
     154          84 :     ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
     155          84 :     if( fd_funk_txn_idx_is_null( child_idx ) ) break;
     156          18 :     fd_funk_txn_t * child = &cache->funk->txn_pool->ele[ child_idx ];
     157          18 :     fd_progcache_txn_cancel_tree( cache, child );
     158          18 :   }
     159          66 :   fd_progcache_txn_cancel_one( cache, txn );
     160          66 : }
     161             : 
     162             : /* Cancels all left/right siblings */
     163             : 
     164             : static void
     165             : fd_progcache_txn_cancel_prev_list( fd_progcache_admin_t * cache,
     166          33 :                                    fd_funk_txn_t *        txn ) {
     167          33 :   ulong self_idx = (ulong)( txn - cache->funk->txn_pool->ele );
     168          33 :   for(;;) {
     169          33 :     ulong prev_idx = fd_funk_txn_idx( txn->sibling_prev_cidx );
     170          33 :     if( FD_UNLIKELY( prev_idx==self_idx ) ) FD_LOG_CRIT(( "detected cycle in fork graph" ));
     171          33 :     if( fd_funk_txn_idx_is_null( prev_idx ) ) break;
     172           0 :     fd_funk_txn_t * sibling = &cache->funk->txn_pool->ele[ prev_idx ];
     173           0 :     fd_progcache_txn_cancel_tree( cache, sibling );
     174           0 :   }
     175          33 : }
     176             : 
     177             : static void
     178             : fd_progcache_txn_cancel_next_list( fd_progcache_admin_t * cache,
     179          81 :                                    fd_funk_txn_t *        txn ) {
     180          81 :   ulong self_idx = (ulong)( txn - cache->funk->txn_pool->ele );
     181          81 :   for(;;) {
     182          81 :     ulong next_idx = fd_funk_txn_idx( txn->sibling_next_cidx );
     183          81 :     if( FD_UNLIKELY( next_idx==self_idx ) ) FD_LOG_CRIT(( "detected cycle in fork graph" ));
     184          81 :     if( fd_funk_txn_idx_is_null( next_idx ) ) break;
     185           0 :     fd_funk_txn_t * sibling = &cache->funk->txn_pool->ele[ next_idx ];
     186           0 :     fd_progcache_txn_cancel_tree( cache, sibling );
     187           0 :   }
     188          81 : }
     189             : 
     190             : void
     191             : fd_progcache_txn_cancel( fd_progcache_admin_t * cache,
     192          48 :                          fd_funk_txn_xid_t const * xid ) {
     193          48 :   fd_funk_t * funk = cache->funk;
     194             : 
     195             :   /* Assume no concurrent access to txn_map */
     196             : 
     197          48 :   fd_funk_txn_map_query_t query[1];
     198          48 :   int query_err = fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, query, 0 );
     199          48 :   if( FD_UNLIKELY( query_err ) ) {
     200           0 :     FD_LOG_CRIT(( "fd_progcache_txn_cancel failed: fd_funk_txn_map_query_try(xid=%lu:%lu) returned (%i-%s)",
     201           0 :                    xid->ul[0], xid->ul[1], query_err, fd_map_strerror( query_err ) ));
     202           0 :   }
     203          48 :   fd_funk_txn_t * txn = fd_funk_txn_map_query_ele( query );
     204             : 
     205          48 :   fd_progcache_txn_cancel_next_list( cache, txn );
     206          48 :   fd_progcache_txn_cancel_tree( cache, txn );
     207          48 : }
     208             : 
     209             : /* fd_progcache_gc_root cleans up a stale "rooted" version of a
     210             :    record. */
     211             : 
     212             : static void
     213             : fd_progcache_gc_root( fd_progcache_admin_t *         cache,
     214          15 :                       fd_funk_xid_key_pair_t const * pair ) {
     215          15 :   fd_funk_t * funk = cache->funk;
     216             : 
     217             :   /* Phase 1: Remove record from map if found */
     218             : 
     219          15 :   fd_funk_rec_query_t query[1];
     220          15 :   int rm_err = fd_funk_rec_map_remove( funk->rec_map, pair, NULL, query, FD_MAP_FLAG_BLOCKING );
     221          15 :   if( rm_err==FD_MAP_ERR_KEY ) return;
     222           6 :   if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", rm_err, fd_map_strerror( rm_err ) ));
     223           6 :   FD_COMPILER_MFENCE();
     224             : 
     225             :   /* Phase 2: Invalidate record */
     226             : 
     227           6 :   fd_funk_rec_t * old_rec = query->ele;
     228           6 :   memset( &old_rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
     229           6 :   FD_COMPILER_MFENCE();
     230             : 
     231             :   /* Phase 3: Free record */
     232             : 
     233           6 :   old_rec->map_next = FD_FUNK_REC_IDX_NULL;
     234           6 :   fd_funk_val_flush( old_rec, funk->alloc, funk->wksp );
     235           6 :   fd_funk_rec_pool_release( funk->rec_pool, old_rec, 1 );
     236           6 :   cache->metrics.gc_root_cnt++;
     237           6 : }
     238             : 
     239             : /* fd_progcache_gc_invalidation cleans up a "cache invalidate" record,
     240             :    which may not exist at the database root. */
     241             : 
     242             : static void
     243             : fd_progcache_gc_invalidation( fd_progcache_admin_t * cache,
     244           9 :                               fd_funk_rec_t *        rec ) {
     245           9 :   fd_funk_t * funk = cache->funk;
     246             : 
     247             :   /* Phase 1: Remove record from map if found */
     248             : 
     249           9 :   fd_funk_xid_key_pair_t pair = rec->pair;
     250           9 :   fd_funk_rec_query_t query[1];
     251           9 :   int rm_err = fd_funk_rec_map_remove( funk->rec_map, &pair, NULL, query, FD_MAP_FLAG_BLOCKING );
     252           9 :   if( rm_err==FD_MAP_ERR_KEY ) return;
     253           9 :   if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", rm_err, fd_map_strerror( rm_err ) ));
     254           9 :   if( FD_UNLIKELY( query->ele!=rec ) ) {
     255           0 :     FD_LOG_CRIT(( "Found record collision in program cache: xid=%lu:%lu key=%016lx%016lx%016lx%016lx ele0=%u ele1=%u",
     256           0 :                   pair.xid->ul[0], pair.xid->ul[1],
     257           0 :                   fd_ulong_bswap( pair.key->ul[0] ),
     258           0 :                   fd_ulong_bswap( pair.key->ul[1] ),
     259           0 :                   fd_ulong_bswap( pair.key->ul[2] ),
     260           0 :                   fd_ulong_bswap( pair.key->ul[3] ),
     261           0 :                   (uint)( query->ele - funk->rec_pool->ele ),
     262           0 :                   (uint)( rec        - funk->rec_pool->ele ) ));
     263           0 :   }
     264             : 
     265             :   /* Phase 2: Invalidate record */
     266             : 
     267           9 :   memset( &rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) );
     268           9 :   FD_COMPILER_MFENCE();
     269             : 
     270             :   /* Phase 3: Free record */
     271             : 
     272           9 :   rec->map_next = FD_FUNK_REC_IDX_NULL;
     273           9 :   fd_funk_val_flush( rec, funk->alloc, funk->wksp );
     274           9 :   fd_funk_rec_pool_release( funk->rec_pool, rec, 1 );
     275           9 : }
     276             : 
     277             : /* fd_progcache_publish_recs publishes all of a progcache's records.
     278             :    It is assumed at this point that the txn has no more concurrent
     279             :    users. */
     280             : 
     281             : static void
     282             : fd_progcache_publish_recs( fd_progcache_admin_t * cache,
     283          33 :                            fd_funk_txn_t *        txn ) {
     284             :   /* Iterate record list */
     285          33 :   uint head = txn->rec_head_idx;
     286          33 :   txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
     287          33 :   txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
     288          48 :   while( !fd_funk_rec_idx_is_null( head ) ) {
     289          15 :     fd_funk_rec_t * rec = &cache->funk->rec_pool->ele[ head ];
     290             : 
     291             :     /* Evict previous value from hash chain */
     292          15 :     fd_funk_xid_key_pair_t pair[1];
     293          15 :     fd_funk_rec_key_copy( pair->key, rec->pair.key );
     294          15 :     fd_funk_txn_xid_set_root( pair->xid );
     295          15 :     fd_progcache_gc_root( cache, pair );
     296          15 :     uint next = rec->next_idx;
     297             : 
     298          15 :     fd_progcache_rec_t * prec = fd_funk_val( rec, cache->funk->wksp );
     299          15 :     FD_TEST( prec );
     300          15 :     if( FD_UNLIKELY( prec->invalidate ) ) {
     301             :       /* Drop cache invalidate records */
     302           9 :       fd_progcache_gc_invalidation( cache, rec );
     303           9 :       cache->metrics.gc_root_cnt++;
     304           9 :     } else {
     305             :       /* Migrate record to root */
     306           6 :       rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     307           6 :       rec->next_idx = FD_FUNK_REC_IDX_NULL;
     308           6 :       fd_funk_txn_xid_t const root = { .ul = { ULONG_MAX, ULONG_MAX } };
     309           6 :       fd_funk_txn_xid_st_atomic( rec->pair.xid, &root );
     310           6 :       cache->metrics.root_cnt++;
     311           6 :     }
     312             : 
     313          15 :     head = next; /* next record */
     314          15 :   }
     315          33 : }
     316             : 
     317             : /* fd_progcache_txn_publish_one merges an in-prep transaction whose
     318             :    parent is the last published, into the parent. */
     319             : 
     320             : static void
     321             : fd_progcache_txn_publish_one( fd_progcache_admin_t * cache,
     322          33 :                               fd_funk_txn_t *        txn ) {
     323          33 :   fd_funk_t * funk = cache->funk;
     324             : 
     325             :   /* Phase 1: Mark transaction as "last published" */
     326             : 
     327          33 :   fd_funk_txn_xid_t const * xid = fd_funk_txn_xid( txn );
     328          33 :   FD_LOG_INFO(( "progcache txn laddr=%p xid %lu:%lu: publish", (void *)txn, txn->xid.ul[0], txn->xid.ul[1] ));
     329          33 :   if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) ) {
     330           0 :     FD_LOG_CRIT(( "fd_progcache_publish failed: txn with xid %lu:%lu is not a child of the last published txn", xid->ul[0], xid->ul[1] ));
     331           0 :   }
     332          33 :   fd_funk_txn_xid_st_atomic( funk->shmem->last_publish, xid );
     333             : 
     334             :   /* Phase 2: Drain users from transaction */
     335             : 
     336          33 :   fd_rwlock_write( txn->lock );
     337          33 :   FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_PUBLISH;
     338             : 
     339             :   /* Phase 3: Migrate records */
     340             : 
     341          33 :   fd_progcache_publish_recs( cache, txn );
     342             : 
     343             :   /* Phase 4: Remove transaction from fork graph
     344             : 
     345             :      Because the transaction has no more records, removing it from the
     346             :      fork graph has no visible side effects to concurrent query ops
     347             :      (always return "no found") or insert ops (refuse to write to a
     348             :      "publish" state txn). */
     349             : 
     350          33 :   { /* Adjust the parent pointers of the children to point to "last published" */
     351          33 :     ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
     352          39 :     while( FD_UNLIKELY( !fd_funk_txn_idx_is_null( child_idx ) ) ) {
     353           6 :       funk->txn_pool->ele[ child_idx ].parent_cidx = fd_funk_txn_cidx( FD_FUNK_TXN_IDX_NULL );
     354           6 :       child_idx = fd_funk_txn_idx( funk->txn_pool->ele[ child_idx ].sibling_next_cidx );
     355           6 :     }
     356          33 :   }
     357             : 
     358             :   /* Phase 5: Remove transaction from index
     359             : 
     360             :      The transaction is now an orphan and won't get any new records. */
     361             : 
     362          33 :   fd_funk_txn_map_query_t query[1];
     363          33 :   int remove_err = fd_funk_txn_map_remove( funk->txn_map, xid, NULL, query, 0 );
     364          33 :   if( FD_UNLIKELY( remove_err!=FD_MAP_SUCCESS ) ) {
     365           0 :     FD_LOG_CRIT(( "fd_progcache_publish failed: fd_funk_txn_map_remove failed: %i-%s", remove_err, fd_map_strerror( remove_err ) ));
     366           0 :   }
     367             : 
     368             :   /* Phase 6: Free transaction object */
     369             : 
     370          33 :   fd_rwlock_unwrite( txn->lock );
     371          33 :   FD_VOLATILE( txn->state ) = FD_FUNK_TXN_STATE_FREE;
     372          33 :   txn->parent_cidx       = UINT_MAX;
     373          33 :   txn->sibling_prev_cidx = UINT_MAX;
     374          33 :   txn->sibling_next_cidx = UINT_MAX;
     375          33 :   txn->child_head_cidx   = UINT_MAX;
     376          33 :   txn->child_tail_cidx   = UINT_MAX;
     377          33 :   fd_funk_txn_pool_release( funk->txn_pool, txn, 1 );
     378          33 : }
     379             : 
     380             : void
     381             : fd_progcache_txn_advance_root( fd_progcache_admin_t *    cache,
     382          33 :                                fd_funk_txn_xid_t const * xid ) {
     383          33 :   fd_funk_t * funk = cache->funk;
     384             : 
     385             :   /* Assume no concurrent access to txn_map */
     386             : 
     387          33 :   fd_funk_txn_map_query_t query[1];
     388          33 :   int query_err = fd_funk_txn_map_query_try( funk->txn_map, xid, NULL, query, 0 );
     389          33 :   if( FD_UNLIKELY( query_err ) ) {
     390           0 :     FD_LOG_CRIT(( "fd_progcache_txn_publish_one failed: fd_funk_txn_map_query_try(xid=%lu:%lu) returned (%i-%s)",
     391           0 :                    xid->ul[0], xid->ul[1], query_err, fd_map_strerror( query_err ) ));
     392           0 :   }
     393          33 :   fd_funk_txn_t * txn = fd_funk_txn_map_query_ele( query );
     394             : 
     395          33 :   if( FD_UNLIKELY( !fd_funk_txn_idx_is_null( fd_funk_txn_idx( txn->parent_cidx ) ) ) ) {
     396           0 :     FD_LOG_CRIT(( "fd_progcache_txn_advance_root: parent of txn %lu:%lu is not root", xid->ul[0], xid->ul[1] ));
     397           0 :   }
     398             : 
     399          33 :   fd_progcache_txn_cancel_prev_list( cache, txn );
     400          33 :   fd_progcache_txn_cancel_next_list( cache, txn );
     401          33 :   txn->sibling_prev_cidx = UINT_MAX;
     402          33 :   txn->sibling_next_cidx = UINT_MAX;
     403             : 
     404             :   /* Children of transaction are now children of root */
     405          33 :   funk->shmem->child_head_cidx = txn->child_head_cidx;
     406          33 :   funk->shmem->child_tail_cidx = txn->child_tail_cidx;
     407             : 
     408          33 :   fd_progcache_txn_publish_one( cache, txn );
     409          33 : }
     410             : 
     411             : /* reset_txn_list does a depth-first traversal of the txn tree.
     412             :    Detaches all recs from txns by emptying rec linked lists. */
     413             : 
     414             : static void
     415             : reset_txn_list( fd_funk_t * funk,
     416           0 :                 ulong       txn_head_idx ) {
     417           0 :   fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
     418           0 :   for( ulong idx = txn_head_idx;
     419           0 :        !fd_funk_txn_idx_is_null( idx );
     420           0 :   ) {
     421           0 :     fd_funk_txn_t * txn = &txn_pool->ele[ idx ];
     422           0 :     fd_funk_txn_state_assert( txn, FD_FUNK_TXN_STATE_ACTIVE );
     423           0 :     txn->rec_head_idx = FD_FUNK_REC_IDX_NULL;
     424           0 :     txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL;
     425           0 :     reset_txn_list( funk, txn->child_head_cidx );
     426           0 :     idx = fd_funk_txn_idx( txn->sibling_next_cidx );
     427           0 :   }
     428           0 : }
     429             : 
     430             : /* reset_rec_map frees all records in a funk instance. */
     431             : 
     432             : static void
     433           0 : reset_rec_map( fd_funk_t * funk ) {
     434           0 :   fd_wksp_t *          wksp     = funk->wksp;
     435           0 :   fd_alloc_t *         alloc    = funk->alloc;
     436           0 :   fd_funk_rec_map_t *  rec_map  = funk->rec_map;
     437           0 :   fd_funk_rec_pool_t * rec_pool = funk->rec_pool;
     438             : 
     439           0 :   ulong chain_cnt = fd_funk_rec_map_chain_cnt( rec_map );
     440           0 :   for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
     441           0 :     for(
     442           0 :         fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter( rec_map, chain_idx );
     443           0 :         !fd_funk_rec_map_iter_done( iter );
     444           0 :     ) {
     445           0 :       fd_funk_rec_t * rec = fd_funk_rec_map_iter_ele( iter );
     446           0 :       ulong next = fd_funk_rec_map_private_idx( rec->map_next );;
     447             : 
     448             :       /* Remove rec object from map */
     449           0 :       fd_funk_rec_map_query_t rec_query[1];
     450           0 :       int err = fd_funk_rec_map_remove( rec_map, fd_funk_rec_pair( rec ), NULL, rec_query, FD_MAP_FLAG_BLOCKING );
     451           0 :       fd_funk_rec_key_t key; fd_funk_rec_key_copy( &key, rec->pair.key );
     452           0 :       if( FD_UNLIKELY( err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed (%i-%s)", err, fd_map_strerror( err ) ));
     453             : 
     454             :       /* Free rec resources */
     455           0 :       fd_funk_val_flush( rec, alloc, wksp );
     456           0 :       fd_funk_rec_pool_release( rec_pool, rec, 1 );
     457           0 :       iter.ele_idx = next;
     458           0 :     }
     459           0 :   }
     460           0 : }
     461             : 
     462             : void
     463           0 : fd_progcache_reset( fd_progcache_admin_t * cache ) {
     464           0 :   fd_funk_t * funk = cache->funk;
     465           0 :   reset_txn_list( funk, fd_funk_txn_idx( funk->shmem->child_head_cidx ) );
     466           0 :   reset_rec_map( funk );
     467           0 : }
     468             : 
     469             : /* clear_txn_list does a depth-first traversal of the txn tree.
     470             :    Removes all txns. */
     471             : 
     472             : static void
     473             : clear_txn_list( fd_funk_t * funk,
     474           0 :                 ulong       txn_head_idx ) {
     475           0 :   fd_funk_txn_pool_t * txn_pool = funk->txn_pool;
     476           0 :   fd_funk_txn_map_t *  txn_map  = funk->txn_map;
     477           0 :   for( ulong idx = txn_head_idx;
     478           0 :        !fd_funk_txn_idx_is_null( idx );
     479           0 :   ) {
     480           0 :     fd_funk_txn_t * txn = &txn_pool->ele[ idx ];
     481           0 :     fd_funk_txn_state_assert( txn, FD_FUNK_TXN_STATE_ACTIVE );
     482           0 :     ulong next_idx  = fd_funk_txn_idx( txn->sibling_next_cidx );
     483           0 :     ulong child_idx = fd_funk_txn_idx( txn->child_head_cidx );
     484           0 :     txn->rec_head_idx      = FD_FUNK_REC_IDX_NULL;
     485           0 :     txn->rec_tail_idx      = FD_FUNK_REC_IDX_NULL;
     486           0 :     txn->child_head_cidx   = UINT_MAX;
     487           0 :     txn->child_tail_cidx   = UINT_MAX;
     488           0 :     txn->parent_cidx       = UINT_MAX;
     489           0 :     txn->sibling_prev_cidx = UINT_MAX;
     490           0 :     txn->sibling_next_cidx = UINT_MAX;
     491           0 :     clear_txn_list( funk, child_idx );
     492           0 :     fd_funk_txn_map_query_t query[1];
     493           0 :     int rm_err = fd_funk_txn_map_remove( txn_map, &txn->xid, NULL, query, FD_MAP_FLAG_BLOCKING );
     494           0 :     if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_txn_map_remove failed (%i-%s)", rm_err, fd_map_strerror( rm_err ) ));
     495           0 :     txn->state = FD_FUNK_TXN_STATE_FREE;
     496           0 :     int free_err = fd_funk_txn_pool_release( txn_pool, txn, 1 );
     497           0 :     if( FD_UNLIKELY( free_err!=FD_POOL_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_txn_pool_release failed (%i)", free_err ));
     498           0 :     idx = next_idx;
     499           0 :   }
     500           0 :   funk->shmem->child_head_cidx = UINT_MAX;
     501           0 :   funk->shmem->child_tail_cidx = UINT_MAX;
     502           0 : }
     503             : 
     504             : void
     505           0 : fd_progcache_clear( fd_progcache_admin_t * cache ) {
     506           0 :   fd_funk_t * funk = cache->funk;
     507           0 :   clear_txn_list( funk, fd_funk_txn_idx( funk->shmem->child_head_cidx ) );
     508           0 :   reset_rec_map( funk );
     509           0 : }
     510             : 
     511             : void
     512          42 : fd_progcache_verify( fd_progcache_admin_t * cache ) {
     513          42 :   FD_TEST( fd_funk_verify( cache->funk )==FD_FUNK_SUCCESS );
     514          42 : }
     515             : 
     516             : void
     517             : fd_progcache_inject_rec( fd_progcache_admin_t *    cache,
     518             :                          void const *              prog_addr,
     519             :                          fd_account_meta_t const * progdata_meta,
     520             :                          fd_features_t const *     features,
     521             :                          ulong                     slot,
     522             :                          uchar *                   scratch,
     523           0 :                          ulong                     scratch_sz ) {
     524             : 
     525             :   /* XID overview:
     526             : 
     527             :      - load_xid:   tip of fork currently being executed
     528             :      - modify_xid: xid in which program was last modified / deployed
     529             :      - txn->xid:   xid in which program cache entry is inserted
     530             : 
     531             :      slot(load_xid) > slot(entry_xid) >= slot(txn->xid) */
     532             : 
     533             :   /* Acquire reference to ELF binary data */
     534             : 
     535           0 :   ulong progdata_sz = progdata_meta->dlen;
     536           0 :   uchar const * progdata = NULL;
     537           0 :   if( !memcmp( progdata_meta->owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) {
     538           0 :     progdata = (uchar const *)fd_account_data( progdata_meta ) + PROGRAMDATA_METADATA_SIZE;
     539           0 :   } else if( !memcmp( progdata_meta->owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) {
     540           0 :     progdata = (uchar const *)fd_account_data( progdata_meta ) + LOADER_V4_PROGRAM_DATA_OFFSET;
     541           0 :   } else if( !memcmp( progdata_meta->owner, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) ||
     542           0 :              !memcmp( progdata_meta->owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) ) {
     543           0 :     progdata = (uchar const *)fd_account_data( progdata_meta );
     544           0 :   }
     545           0 :   if( FD_UNLIKELY( !progdata ) ) return;
     546             : 
     547             :   /* Allocate a funk_rec */
     548             : 
     549           0 :   fd_funk_t * funk = cache->funk;
     550           0 :   fd_funk_rec_t * funk_rec = fd_funk_rec_pool_acquire( funk->rec_pool, NULL, 0, NULL );
     551           0 :   if( FD_UNLIKELY( !funk_rec ) ) {
     552           0 :     FD_LOG_ERR(( "Program cache is out of memory: fd_funk_rec_pool_acquire failed (rec_max=%lu)",
     553           0 :                  fd_funk_rec_pool_ele_max( funk->rec_pool ) ));
     554           0 :   }
     555           0 :   memset( funk_rec, 0, sizeof(fd_funk_rec_t) );
     556           0 :   fd_funk_val_init( funk_rec );
     557             : 
     558           0 :   funk_rec->tag = 0;
     559           0 :   funk_rec->prev_idx = FD_FUNK_REC_IDX_NULL;
     560           0 :   funk_rec->next_idx = FD_FUNK_REC_IDX_NULL;
     561           0 :   memcpy( funk_rec->pair.key, prog_addr, 32UL );
     562           0 :   fd_funk_txn_xid_set_root( funk_rec->pair.xid );
     563             : 
     564             :   /* Load program */
     565             : 
     566           0 :   ulong const load_slot = slot;
     567           0 :   fd_prog_versions_t versions = fd_prog_versions( features, load_slot );
     568           0 :   fd_sbpf_loader_config_t config = {
     569           0 :     .sbpf_min_version = versions.min_sbpf_version,
     570           0 :     .sbpf_max_version = versions.max_sbpf_version,
     571           0 :   };
     572           0 :   fd_sbpf_elf_info_t elf_info[1];
     573             : 
     574           0 :   fd_progcache_rec_t * rec = NULL;
     575           0 :   if( FD_LIKELY( fd_sbpf_elf_peek( elf_info, progdata, progdata_sz, &config )==FD_SBPF_ELF_SUCCESS ) ) {
     576             : 
     577           0 :     fd_funk_t * funk          = cache->funk;
     578           0 :     ulong       rec_align     = fd_progcache_rec_align();
     579           0 :     ulong       rec_footprint = fd_progcache_rec_footprint( elf_info );
     580             : 
     581           0 :     void * rec_mem = fd_funk_val_truncate( funk_rec, funk->alloc, funk->wksp, rec_align, rec_footprint, NULL );
     582           0 :     if( FD_UNLIKELY( !rec_mem ) ) {
     583           0 :       FD_LOG_ERR(( "Program cache is out of memory: fd_alloc_malloc failed (requested align=%lu sz=%lu)",
     584           0 :                   rec_align, rec_footprint ));
     585           0 :     }
     586             : 
     587           0 :     rec = fd_progcache_rec_new( rec_mem, elf_info, &config, load_slot, features, progdata, progdata_sz, scratch, scratch_sz );
     588           0 :     if( !rec ) {
     589           0 :       fd_funk_val_flush( funk_rec, funk->alloc, funk->wksp );
     590           0 :     }
     591           0 :   }
     592             : 
     593             :   /* Convert to tombstone if load failed */
     594             : 
     595           0 :   if( !rec ) { /* load fail */
     596           0 :     void * rec_mem = fd_funk_val_truncate( funk_rec, funk->alloc, funk->wksp, fd_progcache_rec_align(), fd_progcache_rec_footprint( NULL ), NULL );
     597           0 :     if( FD_UNLIKELY( !rec_mem ) ) {
     598           0 :       FD_LOG_ERR(( "Program cache is out of memory: fd_alloc_malloc failed (requested align=%lu sz=%lu)",
     599           0 :                    fd_progcache_rec_align(), fd_progcache_rec_footprint( NULL ) ));
     600           0 :     }
     601           0 :     rec = fd_progcache_rec_new_nx( rec_mem, load_slot );
     602           0 :   }
     603             : 
     604             :   /* Publish cache entry to funk index */
     605             : 
     606           0 :   int insert_err = fd_funk_rec_map_txn_insert( funk->rec_map, funk_rec );
     607           0 :   if( FD_UNLIKELY( insert_err!=FD_MAP_SUCCESS ) ) {
     608           0 :     FD_LOG_CRIT(( "fd_funk_rec_map_txn_insert failed: %i-%s", insert_err, fd_map_strerror( insert_err ) ));
     609           0 :   }
     610           0 : }

Generated by: LCOV version 1.14