Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_runtime_fd_bank_h
2 : #define HEADER_fd_src_flamenco_runtime_fd_bank_h
3 :
4 : #include "../fd_flamenco_base.h"
5 :
6 : #include "../../ballet/lthash/fd_lthash.h"
7 : #include "../../funk/fd_funk.h"
8 :
9 : #include "../types/fd_types.h"
10 : #include "../leaders/fd_leaders.h"
11 : #include "../features/fd_features.h"
12 : #include "../fd_rwlock.h"
13 :
14 : FD_PROTOTYPES_BEGIN
15 :
16 6 : #define FD_BANKS_MAGIC 0X99999AA9999UL
17 :
18 : /* TODO: Some optimizations, cleanups, future work:
19 : 1. Simple data types (ulong, int, etc) should be stored as their
20 : underlying type instead of a byte array.
21 : 2. Remove the slot_ctx entirely and just use the bank struct
22 : directly in the runtime.
23 : 3. For some of the more complex types in the bank, we should provide
24 : a way to layout the data ahead of time instead of manually.
25 : calculating the offsets/layout of the offset-based struct.
26 : This could likely be emitted from fd_types
27 : 4. Perhaps make the query/modify scoping more explicit. Right now,
28 : the caller is free to use the API wrong if there are no locks.
29 : Maybe just expose a different API if there are no locks?
30 : 5. Rename fd_banks_t to fd_bank_mgr_t.
31 : 7. Add locks around the CoW pools.
32 : 8. Rename locks to suffix with _query_locking and _query_locking_end
33 : */
34 :
35 : /* A fd_bank_t struct is the represenation of the bank state on Solana
36 : for a given slot. More specifically, the bank state corresponds to
37 : all information needed during execution that is not stored on-chain,
38 : but is instead cached in a validator's memory. Each of these bank
39 : fields are repesented by a member of the fd_bank_t struct.
40 :
41 : Management of fd_bank_t structs must be fork-aware: the state of each
42 : fd_bank_t must be based on the fd_bank_t of it's parent slot. This
43 : state is managed by the fd_banks_t struct.
44 :
45 : In order to support fork-awareness, there are a few key features
46 : that fd_banks_t and fd_bank_t MUST support:
47 : 1. Query for any non-rooted slot's bank: create a fast lookup
48 : from slot to bank
49 : 2. Be able to create a new bank for a given slot from the bank of
50 : that slot's parent and maintain some tree-like structure to
51 : track the parent-child relationships: copy the contents from a
52 : parent bank into a child bank.
53 : 3. Prune the set of active banks to keep the root updated as the
54 : network progresses: free resources of fd_bank_t structs that
55 : are are not direct descendants of the root bank (remove parents
56 : and any competing lineages).
57 : 4. Each bank will have field(s) that are concurrently read/write
58 : from multiple threads: add read-write locks to the fields that are
59 : concurrently written to.
60 : 5. In practice, a bank state for a given slot can be very large and
61 : not all of the fields are written to every slot. Therefore, it can
62 : be very expensive to copy the entire bank state for a given slot
63 : each time a bank is created. In order to avoid large memcpys, we
64 : can use a CoW mechanism for certain fields.
65 :
66 : Each field of a fd_bank_t has a pre-specified set of fields including
67 : - name: the name of the field
68 : - footprint: the size of the field in bytes
69 : - align: the alignment of the field
70 : - CoW: whether the field is CoW
71 : - has_lock: whether the field has a rw-lock
72 : - type: type of the field
73 :
74 : fd_banks_t is represented by a left-child, right-sibling n-ary tree
75 : (as inspired by fd_ghost) to keep track of the parent-child fork tree.
76 : The underlying data structure is a map of fd_bank_t structs that is
77 : keyed by slot. This map is backed by a simple memory pool.
78 :
79 : Each field in fd_bank_t that is not CoW is laid out contiguously in
80 : the fd_bank_t struct as simple uchar buffers. This allows for a simple
81 : memcpy to clone the bank state from a parent to a child.
82 :
83 : Each field that is CoW has its own memory pool. The memory
84 : corresponding to the field is not located in the fd_bank_t struct and
85 : is instead represented by a pool index and a dirty flag. If the field
86 : is modified, then the dirty flag is set, and an element of the pool
87 : is acquired and the data is copied over from the parent pool idx.
88 :
89 : fd_bank_t also holds all of the rw-locks for the fields that have
90 : rw-locks.
91 :
92 : So, when a bank is cloned from a parent, the non CoW fields are copied
93 : over and the CoW fields just copy over a pool index. The CoW behavior
94 : is completely abstracted away from the caller as callers have to
95 : query/modify fields using specific APIs.
96 :
97 : NOTE: An important invariant is that if a field is CoW, then it must
98 : have a rw-lock.
99 :
100 : The usage pattern is as follows:
101 :
102 : To create an initial bank:
103 : fd_bank_t * bank_init = fd_bank_init_bank( banks, slot );
104 :
105 : To clone bank from parent banks:
106 : fd_bank_t * bank_clone = fd_banks_clone_from_parent( banks, slot, parent_slot );
107 :
108 : To publish a bank (aka update the root bank):
109 : fd_bank_t * bank_publish = fd_banks_publish( banks, slot );
110 :
111 : To query some arbitrary bank:
112 : fd_bank_t * bank_query = fd_banks_get_bank( banks, slot );
113 :
114 : To access fields in the bank if a field does not have a lock:
115 :
116 : fd_struct_t const * field = fd_bank_field_query( bank );
117 : OR
118 : fd_struct field = fd_bank_field_get( bank );
119 :
120 : To modify fields in the bank if a field does not have a lock:
121 :
122 : fd_struct_t * field = fd_bank_field_modify( bank );
123 : OR
124 : fd_bank_field_set( bank, value );
125 :
126 : IMPORTANT SAFETY NOTE: fd_banks_t assumes that there is only one bank
127 : being executed against at a time. However, it is safe to call
128 : fd_banks_publish while threads are executing against a bank.
129 :
130 : */
131 :
132 : /* Define additional fields to the bank struct here. If trying to add
133 : a CoW field to the bank, define a pool for it as done below. */
134 :
135 : #define FD_BANKS_ITER(X) \
136 : /* type, name, footprint, align, CoW, has lock */ \
137 93 : X(fd_clock_timestamp_votes_global_t, clock_timestamp_votes, 5000000UL, 128UL, 1, 1 ) /* TODO: This needs to get sized out */ \
138 93 : X(fd_account_keys_global_t, stake_account_keys, 100000000UL, 128UL, 1, 1 ) /* Supports roughly 3M stake accounts */ \
139 93 : X(fd_account_keys_global_t, vote_account_keys, 3200000UL, 128UL, 1, 1 ) /* Supports roughly 100k vote accounts */ \
140 36 : X(fd_block_hash_queue_global_t, block_hash_queue, 50000UL, 128UL, 0, 0 ) /* Block hash queue */ \
141 36 : X(fd_fee_rate_governor_t, fee_rate_governor, sizeof(fd_fee_rate_governor_t), alignof(fd_fee_rate_governor_t), 0, 0 ) /* Fee rate governor */ \
142 36 : X(ulong, capitalization, sizeof(ulong), alignof(ulong), 0, 0 ) /* Capitalization */ \
143 36 : X(ulong, lamports_per_signature, sizeof(ulong), alignof(ulong), 0, 0 ) /* Lamports per signature */ \
144 36 : X(ulong, prev_lamports_per_signature, sizeof(ulong), alignof(ulong), 0, 0 ) /* Previous lamports per signature */ \
145 36 : X(ulong, transaction_count, sizeof(ulong), alignof(ulong), 0, 0 ) /* Transaction count */ \
146 36 : X(ulong, parent_signature_cnt, sizeof(ulong), alignof(ulong), 0, 0 ) /* Parent signature count */ \
147 36 : X(ulong, tick_height, sizeof(ulong), alignof(ulong), 0, 0 ) /* Tick height */ \
148 36 : X(ulong, max_tick_height, sizeof(ulong), alignof(ulong), 0, 0 ) /* Max tick height */ \
149 36 : X(ulong, hashes_per_tick, sizeof(ulong), alignof(ulong), 0, 0 ) /* Hashes per tick */ \
150 36 : X(uint128, ns_per_slot, sizeof(uint128), alignof(uint128), 0, 0 ) /* NS per slot */ \
151 36 : X(ulong, ticks_per_slot, sizeof(ulong), alignof(ulong), 0, 0 ) /* Ticks per slot */ \
152 36 : X(ulong, genesis_creation_time, sizeof(ulong), alignof(ulong), 0, 0 ) /* Genesis creation time */ \
153 36 : X(double, slots_per_year, sizeof(double), alignof(double), 0, 0 ) /* Slots per year */ \
154 36 : X(fd_inflation_t, inflation, sizeof(fd_inflation_t), alignof(fd_inflation_t), 0, 0 ) /* Inflation */ \
155 36 : X(ulong, total_epoch_stake, sizeof(ulong), alignof(ulong), 0, 0 ) /* Total epoch stake */ \
156 24 : /* This is only used for the get_epoch_stake syscall. */ \
157 24 : /* If we are executing in epoch E, this is the total */ \
158 24 : /* stake at the end of epoch E-1. */ \
159 36 : X(ulong, eah_start_slot, sizeof(ulong), alignof(ulong), 0, 0 ) /* EAH start slot */ \
160 36 : X(ulong, eah_stop_slot, sizeof(ulong), alignof(ulong), 0, 0 ) /* EAH stop slot */ \
161 36 : X(ulong, eah_interval, sizeof(ulong), alignof(ulong), 0, 0 ) /* EAH interval */ \
162 36 : X(ulong, block_height, sizeof(ulong), alignof(ulong), 0, 0 ) /* Block height */ \
163 36 : X(fd_hash_t, epoch_account_hash, sizeof(fd_hash_t), alignof(fd_hash_t), 0, 0 ) /* Epoch account hash */ \
164 36 : X(ulong, execution_fees, sizeof(ulong), alignof(ulong), 0, 0 ) /* Execution fees */ \
165 36 : X(ulong, priority_fees, sizeof(ulong), alignof(ulong), 0, 0 ) /* Priority fees */ \
166 36 : X(ulong, signature_count, sizeof(ulong), alignof(ulong), 0, 0 ) /* Signature count */ \
167 36 : X(ulong, use_prev_epoch_stake, sizeof(ulong), alignof(ulong), 0, 0 ) /* Use prev epoch stake */ \
168 36 : X(fd_hash_t, poh, sizeof(fd_hash_t), alignof(fd_hash_t), 0, 0 ) /* PoH */ \
169 36 : X(fd_sol_sysvar_last_restart_slot_t, last_restart_slot, sizeof(fd_sol_sysvar_last_restart_slot_t), alignof(fd_sol_sysvar_last_restart_slot_t), 0, 0 ) /* Last restart slot */ \
170 36 : X(fd_cluster_version_t, cluster_version, sizeof(fd_cluster_version_t), alignof(fd_cluster_version_t), 0, 0 ) /* Cluster version */ \
171 36 : X(ulong, prev_slot, sizeof(ulong), alignof(ulong), 0, 0 ) /* Previous slot */ \
172 36 : X(fd_hash_t, bank_hash, sizeof(fd_hash_t), alignof(fd_hash_t), 0, 0 ) /* Bank hash */ \
173 36 : X(fd_hash_t, prev_bank_hash, sizeof(fd_hash_t), alignof(fd_hash_t), 0, 0 ) /* Previous bank hash */ \
174 36 : X(fd_hash_t, genesis_hash, sizeof(fd_hash_t), alignof(fd_hash_t), 0, 0 ) /* Genesis hash */ \
175 36 : X(fd_epoch_schedule_t, epoch_schedule, sizeof(fd_epoch_schedule_t), alignof(fd_epoch_schedule_t), 0, 0 ) /* Epoch schedule */ \
176 36 : X(fd_rent_t, rent, sizeof(fd_rent_t), alignof(fd_rent_t), 0, 0 ) /* Rent */ \
177 36 : X(fd_slot_lthash_t, lthash, sizeof(fd_slot_lthash_t), alignof(fd_slot_lthash_t), 0, 0 ) /* LTHash */ \
178 93 : X(fd_vote_accounts_global_t, next_epoch_stakes, 200000000UL, 128UL, 1, 1 ) /* Next epoch stakes, ~4K per account * 50k vote accounts */ \
179 24 : /* These are the stakes that determine the leader */ \
180 24 : /* schedule for the upcoming epoch. If we are executing */ \
181 24 : /* in epoch E, these are the stakes at the end of epoch */ \
182 24 : /* E-1 and they determined the leader schedule for epoch */ \
183 24 : /* E+1. */ \
184 93 : X(fd_vote_accounts_global_t, epoch_stakes, 200000000UL, 128UL, 1, 1 ) /* Epoch stakes ~4K per account * 50k vote accounts */ \
185 93 : X(fd_epoch_reward_status_global_t, epoch_reward_status, 160000000UL, 128UL, 1, 1 ) /* Epoch reward status */ \
186 93 : X(fd_epoch_leaders_t, epoch_leaders, 1000000UL, 128UL, 1, 1 ) /* Epoch leaders */ \
187 93 : X(fd_stakes_global_t, stakes, 400000000UL, 128UL, 1, 1 ) /* Stakes */ \
188 36 : X(fd_features_t, features, sizeof(fd_features_t), alignof(fd_features_t), 0, 0 ) /* Features */ \
189 36 : X(ulong, txn_count, sizeof(ulong), alignof(ulong), 0, 0 ) /* Transaction count */ \
190 36 : X(ulong, nonvote_txn_count, sizeof(ulong), alignof(ulong), 0, 0 ) /* Nonvote transaction count */ \
191 36 : X(ulong, failed_txn_count, sizeof(ulong), alignof(ulong), 0, 0 ) /* Failed transaction count */ \
192 36 : X(ulong, nonvote_failed_txn_count, sizeof(ulong), alignof(ulong), 0, 0 ) /* Nonvote failed transaction count */ \
193 36 : X(ulong, total_compute_units_used, sizeof(ulong), alignof(ulong), 0, 0 ) /* Total compute units used */ \
194 36 : X(ulong, part_width, sizeof(ulong), alignof(ulong), 0, 0 ) /* Part width */ \
195 36 : X(ulong, slots_per_epoch, sizeof(ulong), alignof(ulong), 0, 0 ) /* Slots per epoch */ \
196 36 : X(ulong, shred_cnt, sizeof(ulong), alignof(ulong), 0, 0 ) /* Shred count */ \
197 36 : X(int, enable_exec_recording, sizeof(int), alignof(int), 0, 0 ) /* Enable exec recording */
198 :
199 : /* Invariant Every CoW field must have a rw-lock */
200 : #define X(type, name, footprint, align, cow, has_lock) \
201 : FD_STATIC_ASSERT( (cow == 1 && has_lock == 1) || (cow == 0), CoW fields must have a rw-lock );
202 : FD_BANKS_ITER(X)
203 : #undef X
204 :
205 : /* If a member of the bank is CoW then it needs a corresponding pool
206 : which is defined here. If a type if not a CoW then it does not need
207 : to be in a pool and is laid out contigiously in the bank struct. */
208 :
209 : /* Declare a pool object wrapper for all CoW fields. */
210 : #define HAS_COW_1(name, footprint, align) \
211 : static const ulong fd_bank_##name##_align = align; \
212 : static const ulong fd_bank_##name##_footprint = footprint; \
213 : \
214 : struct fd_bank_##name { \
215 : ulong next; \
216 : uchar data[footprint]__attribute__((aligned(align))); \
217 : }; \
218 : typedef struct fd_bank_##name fd_bank_##name##_t;
219 :
220 : /* Do nothing if CoW is not enabled. */
221 : #define HAS_COW_0(name, footprint, align)
222 :
223 : #define X(type, name, footprint, align, cow, has_lock) \
224 : HAS_COW_##cow(name, footprint, align)
225 : FD_BANKS_ITER(X)
226 :
227 : #undef X
228 : #undef HAS_COW_0
229 : #undef HAS_COW_1
230 :
231 : #define POOL_NAME fd_bank_clock_timestamp_votes_pool
232 63 : #define POOL_T fd_bank_clock_timestamp_votes_t
233 : #include "../../util/tmpl/fd_pool.c"
234 : #undef POOL_NAME
235 : #undef POOL_T
236 :
237 : #define POOL_NAME fd_bank_stake_account_keys_pool
238 63 : #define POOL_T fd_bank_stake_account_keys_t
239 : #include "../../util/tmpl/fd_pool.c"
240 : #undef POOL_NAME
241 : #undef POOL_T
242 :
243 : #define POOL_NAME fd_bank_vote_account_keys_pool
244 63 : #define POOL_T fd_bank_vote_account_keys_t
245 : #include "../../util/tmpl/fd_pool.c"
246 : #undef POOL_NAME
247 : #undef POOL_T
248 :
249 : #define POOL_NAME fd_bank_next_epoch_stakes_pool
250 63 : #define POOL_T fd_bank_next_epoch_stakes_t
251 : #include "../../util/tmpl/fd_pool.c"
252 : #undef POOL_NAME
253 : #undef POOL_T
254 :
255 : #define POOL_NAME fd_bank_epoch_stakes_pool
256 63 : #define POOL_T fd_bank_epoch_stakes_t
257 : #include "../../util/tmpl/fd_pool.c"
258 : #undef POOL_NAME
259 : #undef POOL_T
260 :
261 : #define POOL_NAME fd_bank_epoch_reward_status_pool
262 63 : #define POOL_T fd_bank_epoch_reward_status_t
263 : #include "../../util/tmpl/fd_pool.c"
264 : #undef POOL_NAME
265 : #undef POOL_T
266 :
267 : #define POOL_NAME fd_bank_epoch_leaders_pool
268 63 : #define POOL_T fd_bank_epoch_leaders_t
269 : #include "../../util/tmpl/fd_pool.c"
270 : #undef POOL_NAME
271 : #undef POOL_T
272 :
273 : #define POOL_NAME fd_bank_stakes_pool
274 63 : #define POOL_T fd_bank_stakes_t
275 : #include "../../util/tmpl/fd_pool.c"
276 : #undef POOL_NAME
277 : #undef POOL_T
278 :
279 : /* As mentioned above, the overall layout of the bank struct:
280 : - Fields used for internal pool/bank management
281 : - Non-Cow fields
282 : - CoW fields
283 : - Locks for CoW fields
284 :
285 : The CoW fields are laid out contiguously in the bank struct.
286 : The locks for the CoW fields are laid out contiguously after the
287 : CoW fields.
288 : */
289 :
290 : struct fd_bank {
291 90 : #define FD_BANK_HEADER_SIZE (40UL)
292 :
293 : /* Fields used for internal pool and bank management */
294 : ulong slot; /* slot this node is tracking, also the map key */
295 : ulong next; /* reserved for internal use by fd_pool_para, fd_map_chain_para and fd_banks_publish */
296 : ulong parent_idx; /* index of the parent in the node pool */
297 : ulong child_idx; /* index of the left-child in the node pool */
298 : ulong sibling_idx; /* index of the right-sibling in the node pool */
299 :
300 : /* First, layout all non-CoW fields contiguously. This is done to
301 : allow for cloning the bank state with a simple memcpy. Each
302 : non-CoW field is just represented as a byte array. */
303 :
304 : #define HAS_COW_1(type, name, footprint, align)
305 :
306 : #define HAS_COW_0(type, name, footprint, align) \
307 : uchar name[footprint] __attribute__((aligned(align)));
308 :
309 : #define X(type, name, footprint, align, cow, has_lock) \
310 : HAS_COW_##cow(type, name, footprint, align)
311 : FD_BANKS_ITER(X)
312 : #undef X
313 : #undef HAS_COW_0
314 : #undef HAS_COW_1
315 :
316 : /* Now, layout all information needed for CoW fields. These are only
317 : copied when explicitly requested by the caller. The field's data
318 : is located at teh pool idx in the pool. If the dirty flag has been
319 : set, then the element has been copied over for this bank. */
320 :
321 : #define HAS_COW_1(type, name, footprint, align) \
322 : int name##_dirty; \
323 : ulong name##_pool_idx; \
324 : ulong name##_pool_offset;
325 :
326 : #define HAS_COW_0(type, name, footprint, align)
327 :
328 : #define X(type, name, footprint, align, cow, has_lock) \
329 : HAS_COW_##cow(type, name, footprint, align)
330 : FD_BANKS_ITER(X)
331 : #undef X
332 : #undef HAS_COW_0
333 : #undef HAS_COW_1
334 :
335 : /* Now emit locks for all fields that need a rwlock. */
336 :
337 : #define HAS_LOCK_1(type, name, footprint, align) \
338 : fd_rwlock_t name##_lock;
339 :
340 : #define HAS_LOCK_0(type, name, footprint, align) /* Do nothing for these. */
341 :
342 : #define X(type, name, footprint, align, cow, has_lock) \
343 : HAS_LOCK_##has_lock(type, name, footprint, align)
344 : FD_BANKS_ITER(X)
345 : #undef X
346 : #undef HAS_LOCK_0
347 : #undef HAS_LOCK_1
348 :
349 : };
350 : typedef struct fd_bank fd_bank_t;
351 :
352 : #define HAS_COW_1(type, name, footprint, align) \
353 : static inline void \
354 288 : fd_bank_set_##name##_pool( fd_bank_t * bank, fd_bank_##name##_t * bank_pool ) { \
355 288 : void * bank_pool_mem = fd_bank_##name##_pool_leave( bank_pool ); \
356 288 : if( FD_UNLIKELY( !bank_pool_mem ) ) { \
357 0 : FD_LOG_CRIT(( "Failed to leave bank pool" )); \
358 0 : } \
359 288 : bank->name##_pool_offset = (ulong)bank_pool_mem - (ulong)bank; \
360 288 : } \
361 : static inline fd_bank_##name##_t * \
362 0 : fd_bank_get_##name##_pool( fd_bank_t * bank ) { \
363 0 : return fd_bank_##name##_pool_join( (uchar *)bank + bank->name##_pool_offset ); \
364 0 : }
365 : #define HAS_COW_0(type, name, footprint, align) /* Do nothing for these. */
366 :
367 : #define X(type, name, footprint, align, cow, has_lock) \
368 : HAS_COW_##cow(type, name, footprint, align)
369 : FD_BANKS_ITER(X)
370 : #undef X
371 : #undef HAS_COW_0
372 : #undef HAS_COW_1
373 :
374 :
375 : /* fd_bank_t is the alignment for the bank state. */
376 :
377 : ulong
378 : fd_bank_align( void );
379 :
380 : /* fd_bank_t is the footprint for the bank state. This does NOT
381 : include the footprint for the CoW state. */
382 :
383 : ulong
384 : fd_bank_footprint( void );
385 :
386 : /**********************************************************************/
387 : /* fd_banks_t is the main struct used to manage the bank state. It can
388 : be used to query/modify/clone/publish the bank state.
389 :
390 : fd_banks_t contains some metadata a map/pool pair to manage the banks.
391 : It also contains pointers to the CoW pools.
392 :
393 : The data is laid out contigiously in memory starting from fd_banks_t;
394 : this can be seen in fd_banks_footprint(). */
395 :
396 : #define POOL_NAME fd_banks_pool
397 81 : #define POOL_T fd_bank_t
398 : #include "../../util/tmpl/fd_pool.c"
399 : #undef POOL_NAME
400 : #undef POOL_T
401 :
402 : #define MAP_NAME fd_banks_map
403 : #define MAP_ELE_T fd_bank_t
404 36 : #define MAP_KEY slot
405 : #include "../../util/tmpl/fd_map_chain.c"
406 : #undef MAP_NAME
407 : #undef MAP_ELE_T
408 : #undef MAP_KEY
409 :
410 : struct fd_banks {
411 : ulong magic; /* ==FD_BANKS_MAGIC */
412 : ulong max_banks; /* Maximum number of banks */
413 : ulong root; /* root slot */
414 : ulong root_idx; /* root idx */
415 :
416 : fd_rwlock_t rwlock; /* rwlock for fd_banks_t */
417 :
418 : ulong pool_offset; /* offset of pool from banks */
419 : ulong map_offset; /* offset of map from banks */
420 :
421 : /* Layout all CoW pools. */
422 :
423 : #define HAS_COW_1(type, name, footprint, align) \
424 : ulong name##_pool_offset; /* offset of pool from banks */
425 :
426 : #define HAS_COW_0(type, name, footprint, align) /* Do nothing for these. */
427 :
428 : #define X(type, name, footprint, align, cow, has_lock) \
429 : HAS_COW_##cow(type, name, footprint, align)
430 : FD_BANKS_ITER(X)
431 : #undef X
432 : #undef HAS_COW_0
433 : #undef HAS_COW_1
434 : };
435 : typedef struct fd_banks fd_banks_t;
436 :
437 : /* Bank accesssors */
438 :
439 : #define HAS_LOCK_1(type, name) \
440 : type const * fd_bank_##name##_locking_query( fd_bank_t * bank ); \
441 : void fd_bank_##name##_end_locking_query( fd_bank_t * bank ); \
442 : type * fd_bank_##name##_locking_modify( fd_bank_t * bank ); \
443 : void fd_bank_##name##_end_locking_modify( fd_bank_t * bank );
444 :
445 : #define HAS_LOCK_0(type, name) \
446 : type const * fd_bank_##name##_query( fd_bank_t * bank ); \
447 : type * fd_bank_##name##_modify( fd_bank_t * bank );
448 :
449 : #define X(type, name, footprint, align, cow, has_lock) \
450 : void fd_bank_##name##_set( fd_bank_t * bank, type value ); \
451 : type fd_bank_##name##_get( fd_bank_t * bank ); \
452 : HAS_LOCK_##has_lock(type, name)
453 : FD_BANKS_ITER(X)
454 : #undef X
455 :
456 : #undef HAS_LOCK_0
457 : #undef HAS_LOCK_1
458 :
459 : /* Simple getters and setters for members of fd_banks_t.*/
460 :
461 : static inline fd_bank_t *
462 63 : fd_banks_get_bank_pool( fd_banks_t const * banks ) {
463 63 : return fd_banks_pool_join( ((uchar *)banks + banks->pool_offset) );
464 63 : }
465 :
466 : static inline fd_banks_map_t *
467 60 : fd_banks_get_bank_map( fd_banks_t const * banks ) {
468 60 : return fd_banks_map_join( ((uchar *)banks + banks->map_offset) );
469 60 : }
470 :
471 : static inline void
472 6 : fd_banks_set_bank_pool( fd_banks_t * banks, fd_bank_t * bank_pool ) {
473 6 : void * bank_pool_mem = fd_banks_pool_leave( bank_pool );
474 6 : if( FD_UNLIKELY( !bank_pool_mem ) ) {
475 0 : FD_LOG_CRIT(( "Failed to leave bank pool" ));
476 0 : }
477 6 : banks->pool_offset = (ulong)bank_pool_mem - (ulong)banks;
478 6 : }
479 :
480 : static inline void
481 6 : fd_banks_set_bank_map( fd_banks_t * banks, fd_banks_map_t * bank_map ) {
482 6 : void * bank_map_mem = fd_banks_map_leave( bank_map );
483 6 : if( FD_UNLIKELY( !bank_map_mem ) ) {
484 0 : FD_LOG_CRIT(( "Failed to leave bank map" ));
485 0 : }
486 6 : banks->map_offset = (ulong)bank_map_mem - (ulong)banks;
487 6 : }
488 :
489 : #define HAS_COW_1(type, name, footprint, align) \
490 : static inline fd_bank_##name##_t * \
491 360 : fd_banks_get_##name##_pool( fd_banks_t * banks ) { \
492 360 : return fd_bank_##name##_pool_join( (uchar *)banks + banks->name##_pool_offset ); \
493 360 : } \
494 : static inline void \
495 48 : fd_banks_set_##name##_pool( fd_banks_t * banks, fd_bank_##name##_t * bank_pool ) { \
496 48 : void * bank_pool_mem = fd_bank_##name##_pool_leave( bank_pool ); \
497 48 : if( FD_UNLIKELY( !bank_pool_mem ) ) { \
498 0 : FD_LOG_CRIT(( "Failed to leave bank pool" )); \
499 0 : } \
500 48 : banks->name##_pool_offset = (ulong)bank_pool_mem - (ulong)banks; \
501 48 : }
502 :
503 : #define HAS_COW_0(type, name, footprint, align) /* Do nothing for these. */
504 :
505 : #define X(type, name, footprint, align, cow, has_lock) \
506 : HAS_COW_##cow(type, name, footprint, align)
507 : FD_BANKS_ITER(X)
508 : #undef X
509 : #undef HAS_COW_0
510 : #undef HAS_COW_1
511 :
512 : /* fd_banks_root reutrns the current root slot for the bank. */
513 :
514 : FD_FN_PURE static inline fd_bank_t const *
515 3 : fd_banks_root( fd_banks_t const * banks ) {
516 3 : return fd_banks_pool_ele_const( fd_banks_get_bank_pool( banks ), banks->root_idx );
517 3 : }
518 :
519 : /* fd_banks_align() returns the alignment of fd_banks_t */
520 :
521 : ulong
522 : fd_banks_align( void );
523 :
524 : /* fd_banks_footprint() returns the footprint of fd_banks_t */
525 :
526 : ulong
527 : fd_banks_footprint( ulong max_banks );
528 :
529 : /* fd_banks_new() creates a new fd_banks_t struct. */
530 :
531 : void *
532 : fd_banks_new( void * mem, ulong max_banks );
533 :
534 : /* fd_banks_join() joins a new fd_banks_t struct. */
535 :
536 : fd_banks_t *
537 : fd_banks_join( void * mem );
538 :
539 : /* fd_banks_leave() leaves a bank. */
540 :
541 : void *
542 : fd_banks_leave( fd_banks_t * banks );
543 :
544 : /* fd_banks_delete() deletes a bank. */
545 :
546 : void *
547 : fd_banks_delete( void * shmem );
548 :
549 : /* fd_banks_init_bank() initializes a new bank in the bank manager.
550 : This should only be used during bootup. This returns an initial
551 : fd_bank_t with the corresponding slot. */
552 :
553 : fd_bank_t *
554 : fd_banks_init_bank( fd_banks_t * banks, ulong slot );
555 :
556 : /* fd_bank_get_bank() returns a bank for a given slot. If said bank
557 : does not exist, NULL is returned. */
558 :
559 : fd_bank_t *
560 : fd_banks_get_bank( fd_banks_t * banks, ulong slot );
561 :
562 : /* fd_banks_clone_from_parent() clones a bank from a parent bank.
563 : If the bank corresponding to the parent slot does not exist,
564 : NULL is returned. If a bank is not able to be created, NULL is
565 : returned. The data from the parent bank will copied over into
566 : the new bank.
567 :
568 : A more detailed note: not all of the data is copied over and this
569 : is a shallow clone. All of the CoW fields are not copied over and
570 : will only be done so if the caller explicitly calls
571 : fd_bank_{*}_modify(). This naming was chosen to emulate the
572 : semantics of the Agave client. */
573 :
574 : fd_bank_t *
575 : fd_banks_clone_from_parent( fd_banks_t * banks,
576 : ulong slot,
577 : ulong parent_slot );
578 :
579 : /* fd_banks_publish() publishes a bank to the bank manager. This
580 : should only be used when a bank is no longer needed. This will
581 : prune off the bank from the bank manager. It returns the new root
582 : bank.
583 :
584 : All banks that are ancestors or siblings of the slot will be
585 : cancelled and their resources will be released back to the pool. */
586 :
587 : fd_bank_t const *
588 : fd_banks_publish( fd_banks_t * banks, ulong slot );
589 :
590 : void
591 : fd_bank_clear_bank( fd_bank_t * bank );
592 :
593 : FD_PROTOTYPES_END
594 :
595 : #endif /* HEADER_fd_src_flamenco_runtime_fd_bank_h */
|