Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_progcache_fd_progcache_user_h 2 : #define HEADER_fd_src_flamenco_progcache_fd_progcache_user_h 3 : 4 : /* fd_progcache_user.h provides an API for managing a cache of loaded 5 : Solana on-chain program. 6 : 7 : ### Background 8 : 9 : Solana on-chain programs are rarely updated but frequently executed. 10 : Before a program can be executed, it must be loaded and verified, 11 : which is costly. 12 : 13 : ### Fork management 14 : 15 : The program cache is fork-aware (using funk transactions). Txn-level 16 : operations take an exclusive lock over the cache (record ops are 17 : stalled indefinitely until the txn completes). 18 : 19 : ### Cache entry 20 : 21 : Each Solana program can have a number of program cache entries 22 : (typically only zero or one, in rare cases where the program content 23 : differs across forks multiple). 24 : 25 : A cache entry consists of a funk_rec object (from a preallocated 26 : object pool), and a variable-sized fd_progcache_entry struct 27 : (from an fd_alloc heap). 28 : 29 : ### Cache fill policy 30 : 31 : fd_progcache is lazily filled on reads, and eagerly invalidated 32 : if underlying programs are written to. 33 : 34 : ### Cache evict policy 35 : 36 : Cache eviction (i.e. force removal of potentially useful records) 37 : happens on fill. Specifically, cache eviction is triggered when a 38 : cache fill fails to allocate from the wksp (fd_alloc) heap. 39 : 40 : fd_progcache further has a concept of "generations" (gen). Each 41 : cache fill operation specifies a 'gen' number. Only entries with a 42 : lower 'gen' number may get evicted. 43 : 44 : ### Garbage collect policy 45 : 46 : fd_progcache cleans up unused entries eagerly when: 47 : 48 : 1. a database fork is cancelled (e.g. slot is rooted and competing 49 : history dies, or consensus layer prunes a fork) 50 : 2. a cache entry is orphaned (updated or invalidated by an epoch 51 : boundary) */ 52 : 53 : #include "fd_progcache_rec.h" 54 : #include "fd_prog_load.h" 55 : #include "../accdb/fd_accdb_base.h" 56 : #include "../runtime/fd_runtime_const.h" 57 : #include "../../funk/fd_funk.h" 58 : 59 264 : #define FD_PROGCACHE_DEPTH_MAX (128UL) 60 : 61 : struct fd_progcache_metrics { 62 : ulong fork_switch_cnt; 63 : ulong miss_cnt; 64 : ulong hit_cnt; 65 : ulong hit_tot_sz; 66 : ulong fill_cnt; 67 : ulong fill_tot_sz; 68 : ulong fill_fail_cnt; 69 : ulong dup_insert_cnt; 70 : ulong invalidate_cnt; 71 : }; 72 : 73 : typedef struct fd_progcache_metrics fd_progcache_metrics_t; 74 : 75 : /* fd_progcache_t is a thread-local client to a program cache funk 76 : instance. This struct is quite large and therefore not local/stack 77 : declaration-friendly. */ 78 : 79 : struct fd_progcache { 80 : fd_funk_t funk[1]; 81 : 82 : /* Current fork cache */ 83 : fd_funk_txn_xid_t fork[ FD_PROGCACHE_DEPTH_MAX ]; 84 : ulong fork_depth; 85 : 86 : fd_progcache_metrics_t * metrics; 87 : 88 : uchar * scratch; 89 : ulong scratch_sz; 90 : }; 91 : 92 : typedef struct fd_progcache fd_progcache_t; 93 : 94 : FD_PROTOTYPES_BEGIN 95 : 96 : extern FD_TL fd_progcache_metrics_t fd_progcache_metrics_default; 97 : 98 : /* Constructor */ 99 : 100 : static inline ulong 101 0 : fd_progcache_align( void ) { 102 0 : return alignof(fd_progcache_t); 103 0 : } 104 : 105 : static inline ulong 106 0 : fd_progcache_footprint( void ) { 107 0 : return sizeof(fd_progcache_t); 108 0 : } 109 : 110 : static inline fd_progcache_t * 111 0 : fd_progcache_new( void * ljoin ) { 112 0 : return ljoin; 113 0 : } 114 : 115 : static inline void * 116 0 : fd_progcache_delete( void * ljoin ) { 117 0 : return ljoin; 118 0 : } 119 : 120 : /* fd_progcache_join joins the caller to a program cache funk instance. 121 : scratch points to a FD_PROGCACHE_SCRATCH_ALIGN aligned scratch buffer 122 : and scratch_sz is the size of the largest program/ELF binary that is 123 : going to be loaded (typically max account data sz). */ 124 : 125 : fd_progcache_t * 126 : fd_progcache_join( fd_progcache_t * ljoin, 127 : void * shfunk, 128 : uchar * scratch, 129 : ulong scratch_sz ); 130 : 131 0 : #define FD_PROGCACHE_SCRATCH_ALIGN (64UL) 132 0 : #define FD_PROGCACHE_SCRATCH_FOOTPRINT FD_RUNTIME_ACC_SZ_MAX 133 : 134 : /* fd_progcache_leave detaches the caller from a program cache. */ 135 : 136 : void * 137 : fd_progcache_leave( fd_progcache_t * cache, 138 : void ** opt_shfunk ); 139 : 140 : /* Record-level operations ********************************************/ 141 : 142 : /* fd_progcache_peek queries the program cache for an existing cache 143 : entry. Does not fill the cache. Returns a pointer to the entry on 144 : cache hit (invalidated by the next non-const API call). Returns NULL 145 : on cache miss. */ 146 : 147 : fd_progcache_rec_t const * 148 : fd_progcache_peek( fd_progcache_t * cache, 149 : fd_funk_txn_xid_t const * xid, 150 : void const * prog_addr, 151 : ulong epoch_slot0 ); 152 : 153 : /* fd_progcache_pull loads a program from cache, filling the cache if 154 : necessary. The load operation can have a number of outcomes: 155 : - Returns a pointer to an existing cache entry (cache hit, state 156 : either "Loaded" or "FailedVerification") 157 : - Returns a pointer to a newly created cache entry (cache fill, 158 : state either "Loaded" or "FailedVerification") 159 : - Returns NULL if the requested program account is not deployed (i.e. 160 : account is missing, the program is under visibility delay, or user 161 : has not finished uploading the program) 162 : In other words, this method guarantees to return a cache entry if a 163 : deployed program was found in the account database, and the program 164 : either loaded successfully, or failed ELF/bytecode verification. 165 : Or it returns */ 166 : 167 : fd_progcache_rec_t const * 168 : fd_progcache_pull( fd_progcache_t * cache, 169 : fd_accdb_user_t * accdb, 170 : fd_funk_txn_xid_t const * xid, 171 : void const * prog_addr, 172 : fd_prog_load_env_t const * env ); 173 : 174 : /* fd_progcache_invalidate marks the program at the given address as 175 : invalidated (typically due to a change of program content). This 176 : creates a non-executable cache entry at the given xid. 177 : 178 : After a program has been invalidated at xid, it is forbidden to pull 179 : the same entry at the same xid. (Invalidations should happen after 180 : replaying transactions). */ 181 : 182 : fd_progcache_rec_t const * 183 : fd_progcache_invalidate( fd_progcache_t * cache, 184 : fd_funk_txn_xid_t const * xid, 185 : void const * prog_addr, 186 : ulong slot ); 187 : 188 : FD_PROTOTYPES_END 189 : 190 : #endif /* HEADER_fd_src_flamenco_fd_progcache_h */