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 "../../funk/fd_funk.h" 56 : #include "../runtime/fd_runtime_const.h" 57 : 58 240 : #define FD_PROGCACHE_DEPTH_MAX (128UL) 59 : 60 : struct fd_progcache_metrics { 61 : ulong fork_switch_cnt; 62 : ulong miss_cnt; 63 : ulong hit_cnt; 64 : ulong hit_tot_sz; 65 : ulong fill_cnt; 66 : ulong fill_tot_sz; 67 : ulong fill_fail_cnt; 68 : ulong dup_insert_cnt; 69 : ulong invalidate_cnt; 70 : }; 71 : 72 : typedef struct fd_progcache_metrics fd_progcache_metrics_t; 73 : 74 : /* fd_progcache_t is a thread-local client to a program cache funk 75 : instance. This struct is quite large and therefore not local/stack 76 : declaration-friendly. */ 77 : 78 : struct fd_progcache { 79 : fd_funk_t funk[1]; 80 : 81 : /* Current fork cache */ 82 : fd_funk_txn_xid_t fork[ FD_PROGCACHE_DEPTH_MAX ]; 83 : ulong fork_depth; 84 : 85 : fd_progcache_metrics_t * metrics; 86 : 87 : uchar * scratch; 88 : ulong scratch_sz; 89 : }; 90 : 91 : typedef struct fd_progcache fd_progcache_t; 92 : 93 : FD_PROTOTYPES_BEGIN 94 : 95 : extern FD_TL fd_progcache_metrics_t fd_progcache_metrics_default; 96 : 97 : /* Constructor */ 98 : 99 : static inline ulong 100 0 : fd_progcache_align( void ) { 101 0 : return alignof(fd_progcache_t); 102 0 : } 103 : 104 : static inline ulong 105 0 : fd_progcache_footprint( void ) { 106 0 : return sizeof(fd_progcache_t); 107 0 : } 108 : 109 : static inline fd_progcache_t * 110 0 : fd_progcache_new( void * ljoin ) { 111 0 : return ljoin; 112 0 : } 113 : 114 : static inline void * 115 0 : fd_progcache_delete( void * ljoin ) { 116 0 : return ljoin; 117 0 : } 118 : 119 : /* fd_progcache_join joins the caller to a program cache funk instance. 120 : scratch points to a FD_PROGCACHE_SCRATCH_ALIGN aligned scratch buffer 121 : and scratch_sz is the size of the largest program/ELF binary that is 122 : going to be loaded (typically max account data sz). */ 123 : 124 : fd_progcache_t * 125 : fd_progcache_join( fd_progcache_t * ljoin, 126 : void * shfunk, 127 : uchar * scratch, 128 : ulong scratch_sz ); 129 : 130 0 : #define FD_PROGCACHE_SCRATCH_ALIGN (64UL) 131 0 : #define FD_PROGCACHE_SCRATCH_FOOTPRINT FD_RUNTIME_ACC_SZ_MAX 132 : 133 : /* fd_progcache_leave detaches the caller from a program cache. */ 134 : 135 : void * 136 : fd_progcache_leave( fd_progcache_t * cache, 137 : void ** opt_shfunk ); 138 : 139 : /* Record-level operations ********************************************/ 140 : 141 : /* fd_progcache_peek queries the program cache for an existing cache 142 : entry. Does not fill the cache. Returns a pointer to the entry on 143 : cache hit (invalidated by the next non-const API call). Returns NULL 144 : on cache miss. */ 145 : 146 : fd_progcache_rec_t const * 147 : fd_progcache_peek( fd_progcache_t * cache, 148 : fd_funk_txn_xid_t const * xid, 149 : void const * prog_addr, 150 : ulong epoch_slot0 ); 151 : 152 : /* fd_progcache_pull loads a program from cache, filling the cache if 153 : necessary. The load operation can have a number of outcomes: 154 : - Returns a pointer to an existing cache entry (cache hit, state 155 : either "Loaded" or "FailedVerification") 156 : - Returns a pointer to a newly created cache entry (cache fill, 157 : state either "Loaded" or "FailedVerification") 158 : - Returns NULL if the requested program account is not deployed (i.e. 159 : account is missing, the program is under visibility delay, or user 160 : has not finished uploading the program) 161 : In other words, this method guarantees to return a cache entry if a 162 : deployed program was found in the account database, and the program 163 : either loaded successfully, or failed ELF/bytecode verification. 164 : Or it returns */ 165 : 166 : fd_progcache_rec_t const * 167 : fd_progcache_pull( fd_progcache_t * cache, 168 : fd_funk_t * accdb, 169 : fd_funk_txn_xid_t const * xid, 170 : void const * prog_addr, 171 : fd_prog_load_env_t const * env ); 172 : 173 : /* fd_progcache_invalidate marks the program at the given address as 174 : invalidated (typically due to a change of program content). This 175 : creates a non-executable cache entry at the given xid. 176 : 177 : After a program has been invalidated at xid, it is forbidden to pull 178 : the same entry at the same xid. (Invalidations should happen after 179 : replaying transactions). */ 180 : 181 : fd_progcache_rec_t const * 182 : fd_progcache_invalidate( fd_progcache_t * cache, 183 : fd_funk_txn_xid_t const * xid, 184 : void const * prog_addr, 185 : ulong slot ); 186 : 187 : FD_PROTOTYPES_END 188 : 189 : #endif /* HEADER_fd_src_flamenco_fd_progcache_h */