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 transactions). Txn-level
16 : operations (attach/publish/cancel) take the fork graph's exclusive
17 : lock; a read on the cached lineage never touches it, while switching
18 : fork, publishing and the eviction sweep take it shared.
19 :
20 : ### Cache entry
21 :
22 : Each Solana program can have a number of program cache entries
23 : (typically only zero or one, in rare cases where the program content
24 : differs across forks multiple).
25 :
26 : A cache entry is a progcache_rec object. Records are partitioned by
27 : size class and double as value slots: an executable entry's program
28 : data lives in its own class's arena slot (see fd_progcache.h).
29 :
30 : ### Cache fill policy
31 :
32 : fd_progcache is lazily filled on reads. Writes do not invalidate
33 : the cache; coherence comes from keying records on deploy_slot plus
34 : fork cancel, and from the BPF loader's pd_write gate failing an
35 : invoke before fd_progcache_pull if the programdata was superseded
36 : this slot.
37 :
38 : ### Cache evict policy
39 :
40 : Cache eviction (i.e. force removal of potentially useful records)
41 : happens on fill: when a fill finds its size class full, it evicts
42 : within that class (per-class CLOCK), and falls back to the spill
43 : scratch if no record frees up. The replay tile's housekeeping runs
44 : the same sweep to keep a few slots free per class.
45 :
46 : ### Garbage collect policy
47 :
48 : When a database fork is cancelled (a competing history dies, or the
49 : consensus layer prunes a fork), its records are unmapped at once and
50 : their slots recovered by the next sweep. A superseded revision stays
51 : mapped until CLOCK evicts it. */
52 :
53 : #include "fd_progcache.h"
54 : #include "fd_prog_load.h"
55 : #include "fd_progcache_lineage.h"
56 : #include "../runtime/fd_runtime_const.h"
57 :
58 : struct fd_progcache_metrics {
59 : ulong lookup_cnt;
60 : ulong hit_cnt;
61 : ulong miss_cnt;
62 : ulong hit_loading_cnt;
63 : ulong class_full_cnt;
64 : ulong fill_cnt;
65 : ulong fill_tot_sz;
66 : ulong spill_cnt;
67 : ulong spill_tot_sz;
68 : ulong evict_cnt;
69 : ulong evict_tot_sz;
70 : ulong cum_pull_ticks;
71 : ulong load_cnt;
72 : ulong cum_load_ticks;
73 : /* Per-size-class breakdowns. */
74 : ulong hit_per_class [ FD_PROGCACHE_CACHE_CLASS_CNT ];
75 : ulong fill_per_class [ FD_PROGCACHE_CACHE_CLASS_CNT ];
76 : ulong evict_per_class[ FD_PROGCACHE_CACHE_CLASS_CNT ];
77 : ulong spill_per_class[ FD_PROGCACHE_CACHE_CLASS_CNT ];
78 : };
79 :
80 :
81 : /* fd_progcache_t is a thread-local client to a program cache instance.
82 : This struct is quite large and therefore not local/stack
83 : declaration-friendly. */
84 :
85 : struct fd_progcache {
86 : fd_progcache_join_t join[1];
87 : fd_progcache_lineage_t lineage[1];
88 :
89 : fd_progcache_metrics_t * metrics;
90 :
91 : uchar * scratch;
92 : ulong scratch_sz;
93 :
94 : uint spill_active;
95 : };
96 :
97 : /* Writes every progcache counter for a tile. */
98 :
99 0 : #define FD_PROGCACHE_METRICS_WRITE( TILE, m ) do { \
100 0 : fd_progcache_metrics_t const * _m = (m); \
101 0 : FD_MCNT_SET( TILE, PROGCACHE_LOOKUP, _m->lookup_cnt ); \
102 0 : FD_MCNT_SET( TILE, PROGCACHE_HIT, _m->hit_cnt ); \
103 0 : FD_MCNT_SET( TILE, PROGCACHE_MISS, _m->miss_cnt ); \
104 0 : FD_MCNT_SET( TILE, PROGCACHE_HIT_LOADING, _m->hit_loading_cnt ); \
105 0 : FD_MCNT_SET( TILE, PROGCACHE_CLASS_FULL, _m->class_full_cnt ); \
106 0 : FD_MCNT_SET( TILE, PROGCACHE_FILL, _m->fill_cnt ); \
107 0 : FD_MCNT_SET( TILE, PROGCACHE_FILL_BYTES, _m->fill_tot_sz ); \
108 0 : FD_MCNT_SET( TILE, PROGCACHE_SPILL, _m->spill_cnt ); \
109 0 : FD_MCNT_SET( TILE, PROGCACHE_SPILL_BYTES, _m->spill_tot_sz ); \
110 0 : FD_MCNT_SET( TILE, PROGCACHE_EVICTION, _m->evict_cnt ); \
111 0 : FD_MCNT_SET( TILE, PROGCACHE_EVICTION_BYTES, _m->evict_tot_sz ); \
112 0 : FD_MCNT_SET( TILE, PROGCACHE_DURATION_SECONDS, _m->cum_pull_ticks ); \
113 0 : FD_MCNT_SET( TILE, PROGCACHE_LOAD, _m->load_cnt ); \
114 0 : FD_MCNT_SET( TILE, PROGCACHE_LOAD_DURATION_SECONDS, _m->cum_load_ticks ); \
115 0 : FD_MCNT_ENUM_COPY( TILE, PROGCACHE_CLASS_HIT, _m->hit_per_class ); \
116 0 : FD_MCNT_ENUM_COPY( TILE, PROGCACHE_CLASS_FILL, _m->fill_per_class ); \
117 0 : FD_MCNT_ENUM_COPY( TILE, PROGCACHE_CLASS_EVICTION, _m->evict_per_class ); \
118 0 : FD_MCNT_ENUM_COPY( TILE, PROGCACHE_CLASS_SPILL, _m->spill_per_class ); \
119 0 : } while(0)
120 :
121 : FD_PROTOTYPES_BEGIN
122 :
123 : extern FD_TL fd_progcache_metrics_t fd_progcache_metrics_default;
124 :
125 : /* Constructor */
126 :
127 : /* fd_progcache_join joins the caller to a program cache shmem instance.
128 : scratch points to a FD_PROGCACHE_SCRATCH_ALIGN aligned scratch buffer
129 : and scratch_sz is the size of the largest program/ELF binary that is
130 : going to be loaded (typically max account data sz). */
131 :
132 : fd_progcache_t *
133 : fd_progcache_join( fd_progcache_t * ljoin,
134 : fd_progcache_shmem_t * shmem,
135 : uchar * scratch,
136 : ulong scratch_sz );
137 :
138 60 : #define FD_PROGCACHE_SCRATCH_ALIGN (64UL)
139 60 : #define FD_PROGCACHE_SCRATCH_FOOTPRINT FD_RUNTIME_ACC_SZ_MAX
140 :
141 : /* fd_progcache_leave detaches the caller from a program cache. */
142 :
143 : void *
144 : fd_progcache_leave( fd_progcache_t * cache,
145 : fd_progcache_shmem_t ** opt_shmem );
146 :
147 : /* fd_progcache_pull loads a program from cache, filling the cache if
148 : necessary. The load operation can have a number of outcomes:
149 : - Returns a pointer to an existing cache entry (cache hit, state
150 : either "Loaded" or "FailedVerification")
151 : - Returns a pointer to a newly created cache entry (cache fill,
152 : state either "Loaded" or "FailedVerification")
153 : - Returns NULL if fd_prog_info rejects the account (not a deployed
154 : program of a known loader)
155 : In other words, this method guarantees to return a cache entry if a
156 : deployed program was found in the account database, and the program
157 : either loaded successfully, or failed ELF/bytecode verification.
158 : It is the caller's responsibility to release the returned record with
159 : fd_progcache_rec_close. */
160 :
161 : fd_progcache_rec_t * /* read locked */
162 : fd_progcache_pull( fd_progcache_t * cache,
163 : fd_progcache_fork_id_t fork_id,
164 : fd_pubkey_t const * prog_addr,
165 : fd_prog_load_env_t const * env,
166 : fd_acc_t const * progdata_ro );
167 :
168 : /* fd_progcache_rec_close releases a cache record handle returned by
169 : fd_progcache_pull. */
170 :
171 : void
172 : fd_progcache_rec_close( fd_progcache_t * cache,
173 : fd_progcache_rec_t * rec );
174 :
175 : FD_PROTOTYPES_END
176 :
177 : #endif /* HEADER_fd_src_flamenco_progcache_fd_progcache_user_h */
|