Line data Source code
1 : #include "fd_bank.h"
2 : #include "fd_runtime_const.h"
3 :
4 : ulong
5 0 : fd_bank_align( void ) {
6 0 : return alignof(fd_bank_t);
7 0 : }
8 :
9 : ulong
10 0 : fd_bank_footprint( void ) {
11 0 : ulong l = FD_LAYOUT_INIT;
12 0 : l = FD_LAYOUT_APPEND( l, fd_bank_align(), sizeof(fd_bank_t) );
13 0 : return FD_LAYOUT_FINI( l, fd_bank_align() );
14 0 : }
15 :
16 : /* Bank accesssors */
17 :
18 : #define HAS_COW_1(type, name, footprint, align, has_lock) \
19 : type const * \
20 27 : fd_bank_##name##_locking_query( fd_bank_t * bank ) { \
21 27 : fd_rwlock_read( &bank->name##_lock ); \
22 27 : /* If the pool element hasn't been setup yet, then return NULL */ \
23 27 : fd_bank_##name##_t * name##_pool = fd_bank_get_##name##_pool( bank ); \
24 27 : if( FD_UNLIKELY( name##_pool==NULL ) ) { \
25 0 : FD_LOG_CRIT(( "NULL " #name " pool" )); \
26 0 : } \
27 27 : if( bank->name##_pool_idx==fd_bank_##name##_pool_idx_null( name##_pool ) ) { \
28 6 : return NULL; \
29 6 : } \
30 27 : fd_bank_##name##_t * bank_##name = fd_bank_##name##_pool_ele( name##_pool, bank->name##_pool_idx ); \
31 21 : return (type *)bank_##name->data; \
32 27 : } \
33 : void \
34 27 : fd_bank_##name##_end_locking_query( fd_bank_t * bank ) { \
35 27 : fd_rwlock_unread( &bank->name##_lock ); \
36 27 : } \
37 : type * \
38 21 : fd_bank_##name##_locking_modify( fd_bank_t * bank ) { \
39 21 : fd_rwlock_write( &bank->name##_lock ); \
40 21 : /* If the dirty flag is set, then we already have a pool element */ \
41 21 : /* that was copied over for the current bank. We can simply just */ \
42 21 : /* query the pool element and return it. */ \
43 21 : fd_bank_##name##_t * name##_pool = fd_bank_get_##name##_pool( bank ); \
44 21 : if( FD_UNLIKELY( name##_pool==NULL ) ) { \
45 0 : FD_LOG_CRIT(( "NULL " #name " pool" )); \
46 0 : } \
47 21 : if( bank->name##_dirty ) { \
48 3 : fd_bank_##name##_t * bank_##name = fd_bank_##name##_pool_ele( name##_pool, bank->name##_pool_idx ); \
49 3 : return (type *)bank_##name->data; \
50 3 : } \
51 21 : if( FD_UNLIKELY( !fd_bank_##name##_pool_free( name##_pool ) ) ) { \
52 0 : FD_LOG_CRIT(( "Failed to acquire " #name " pool element: pool is full" )); \
53 0 : } \
54 18 : fd_rwlock_write( fd_bank_get_##name##_pool_lock( bank ) ); \
55 18 : fd_bank_##name##_t * child_##name = fd_bank_##name##_pool_ele_acquire( name##_pool ); \
56 18 : if( FD_UNLIKELY( !child_##name ) ) { \
57 0 : FD_LOG_CRIT(( "Failed to acquire " #name " pool element" )); \
58 0 : } \
59 18 : fd_rwlock_unwrite( fd_bank_get_##name##_pool_lock( bank ) ); \
60 18 : /* If the dirty flag has not been set yet, we need to allocated a */ \
61 18 : /* new pool element and copy over the data from the parent idx. */ \
62 18 : /* We also need to mark the dirty flag. */ \
63 18 : ulong child_idx = fd_bank_##name##_pool_idx( name##_pool, child_##name ); \
64 18 : if( bank->name##_pool_idx!=fd_bank_##name##_pool_idx_null( name##_pool ) ) { \
65 6 : fd_bank_##name##_t * parent_##name = fd_bank_##name##_pool_ele( name##_pool, bank->name##_pool_idx ); \
66 6 : fd_memcpy( child_##name->data, parent_##name->data, fd_bank_##name##_footprint ); \
67 6 : } \
68 18 : bank->name##_pool_idx = child_idx; \
69 18 : bank->name##_dirty = 1; \
70 18 : return (type *)child_##name->data; \
71 18 : } \
72 : void \
73 21 : fd_bank_##name##_end_locking_modify( fd_bank_t * bank ) { \
74 21 : fd_rwlock_unwrite( &bank->name##_lock ); \
75 21 : }
76 :
77 : #define HAS_LOCK_0(type, name) \
78 : type const * \
79 1365 : fd_bank_##name##_query( fd_bank_t const * bank ) { \
80 1365 : return (type const *)fd_type_pun_const( bank->non_cow.name ); \
81 1365 : } \
82 : type * \
83 912 : fd_bank_##name##_modify( fd_bank_t * bank ) { \
84 912 : return (type *)fd_type_pun( bank->non_cow.name ); \
85 912 : }
86 :
87 : #define HAS_LOCK_1(type, name) \
88 : type const * \
89 0 : fd_bank_##name##_locking_query( fd_bank_t * bank ) { \
90 0 : fd_rwlock_read( &bank->name##_lock ); \
91 0 : return (type const *)fd_type_pun_const( bank->non_cow.name ); \
92 0 : } \
93 : type * \
94 462 : fd_bank_##name##_locking_modify( fd_bank_t * bank ) { \
95 462 : fd_rwlock_write( &bank->name##_lock ); \
96 462 : return (type *)fd_type_pun( bank->non_cow.name ); \
97 462 : } \
98 : void \
99 0 : fd_bank_##name##_end_locking_query( fd_bank_t * bank ) { \
100 0 : fd_rwlock_unread( &bank->name##_lock ); \
101 0 : } \
102 : void \
103 462 : fd_bank_##name##_end_locking_modify( fd_bank_t * bank ) { \
104 462 : fd_rwlock_unwrite( &bank->name##_lock ); \
105 462 : }
106 :
107 : #define HAS_COW_0(type, name, footprint, align, has_lock) \
108 : HAS_LOCK_##has_lock(type, name) \
109 : void \
110 1002 : fd_bank_##name##_set( fd_bank_t * bank, type value ) { \
111 1002 : FD_STORE( type, bank->non_cow.name, value ); \
112 1002 : } \
113 : type \
114 981 : fd_bank_##name##_get( fd_bank_t const * bank ) { \
115 981 : type val = FD_LOAD( type, bank->non_cow.name ); \
116 981 : return val; \
117 981 : }
118 :
119 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
120 : HAS_COW_##cow(type, name, footprint, align, has_lock)
121 6 : FD_BANKS_ITER(X)
122 6 : #undef X
123 6 : #undef HAS_COW_0
124 6 : #undef HAS_COW_1
125 6 : #undef HAS_LOCK_0
126 6 : #undef HAS_LOCK_1
127 6 :
128 6 : /**********************************************************************/
129 6 :
130 6 : ulong
131 135 : fd_banks_align( void ) {
132 : /* TODO: The magic number here can probably be removed. */
133 135 : return 128UL;
134 135 : }
135 :
136 : ulong
137 : fd_banks_footprint( ulong max_total_banks,
138 12 : ulong max_fork_width ) {
139 :
140 : /* max_fork_width is used in the macro below. */
141 :
142 12 : ulong l = FD_LAYOUT_INIT;
143 12 : l = FD_LAYOUT_APPEND( l, fd_banks_align(), sizeof(fd_banks_t) );
144 12 : l = FD_LAYOUT_APPEND( l, fd_banks_pool_align(), fd_banks_pool_footprint( max_total_banks ) );
145 12 : l = FD_LAYOUT_APPEND( l, fd_bank_cost_tracker_pool_align(), fd_bank_cost_tracker_pool_footprint( max_fork_width ) );
146 :
147 : /* Need to count the footprint for all of the CoW pools. The footprint
148 : on each CoW pool depends on if the field limits the fork width. */
149 :
150 12 : #define HAS_COW_1_LIMIT_1(name) \
151 48 : l = FD_LAYOUT_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( max_fork_width ) );
152 :
153 12 : #define HAS_COW_1_LIMIT_0(name) \
154 12 : l = FD_LAYOUT_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( max_total_banks ) );
155 :
156 : /* Do nothing for these. */
157 12 : #define HAS_COW_0_LIMIT_0(name)
158 :
159 12 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
160 60 : HAS_COW_##cow##_LIMIT_##limit_fork_width(name)
161 60 : FD_BANKS_ITER(X)
162 12 : #undef X
163 12 : #undef HAS_COW_0_LIMIT_0
164 12 : #undef HAS_COW_1_LIMIT_0
165 12 : #undef HAS_COW_1_LIMIT_1
166 :
167 12 : return FD_LAYOUT_FINI( l, fd_banks_align() );
168 12 : }
169 :
170 : void *
171 6 : fd_banks_new( void * shmem, ulong max_total_banks, ulong max_fork_width ) {
172 :
173 6 : fd_banks_t * banks = (fd_banks_t *)shmem;
174 :
175 6 : if( FD_UNLIKELY( !banks ) ) {
176 0 : FD_LOG_WARNING(( "NULL banks" ));
177 0 : return NULL;
178 0 : }
179 :
180 6 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)banks, fd_banks_align() ) ) ) {
181 0 : FD_LOG_WARNING(( "misaligned banks" ));
182 0 : return NULL;
183 0 : }
184 :
185 : /* Set the rwlock to unlocked. */
186 6 : fd_rwlock_unwrite( &banks->rwlock );
187 :
188 : /* First, layout the banks and the pool used by fd_banks_t. */
189 6 : FD_SCRATCH_ALLOC_INIT( l, banks );
190 6 : banks = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_align(), sizeof(fd_banks_t) );
191 6 : void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_pool_align(), fd_banks_pool_footprint( max_total_banks ) );
192 6 : void * cost_tracker_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_cost_tracker_pool_align(), fd_bank_cost_tracker_pool_footprint( max_fork_width ) );
193 :
194 : /* Need to layout all of the CoW pools. */
195 0 : #define HAS_COW_1_LIMIT_1(name) \
196 24 : void * name##_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( max_fork_width ) );
197 :
198 0 : #define HAS_COW_1_LIMIT_0(name) \
199 6 : void * name##_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( max_total_banks ) );
200 :
201 : /* Do nothing for these. */
202 0 : #define HAS_COW_0_LIMIT_0(name)
203 :
204 0 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
205 30 : HAS_COW_##cow##_LIMIT_##limit_fork_width(name)
206 30 : FD_BANKS_ITER(X)
207 0 : #undef X
208 0 : #undef HAS_COW_0_LIMIT_0
209 0 : #undef HAS_COW_1_LIMIT_0
210 0 : #undef HAS_COW_1_LIMIT_1
211 :
212 6 : if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_banks_align() ) != (ulong)banks + fd_banks_footprint( max_total_banks, max_fork_width ) ) ) {
213 0 : FD_LOG_WARNING(( "fd_banks_new: bad layout" ));
214 0 : return NULL;
215 0 : }
216 :
217 6 : void * pool = fd_banks_pool_new( pool_mem, max_total_banks );
218 6 : if( FD_UNLIKELY( !pool ) ) {
219 0 : FD_LOG_WARNING(( "Failed to create bank pool" ));
220 0 : return NULL;
221 0 : }
222 :
223 6 : fd_bank_t * bank_pool = fd_banks_pool_join( pool );
224 6 : if( FD_UNLIKELY( !bank_pool ) ) {
225 0 : FD_LOG_WARNING(( "Failed to join bank pool" ));
226 0 : return NULL;
227 0 : }
228 :
229 : /* Mark all of the banks as not initialized. */
230 102 : for( ulong i=0UL; i<max_total_banks; i++ ) {
231 96 : fd_bank_t * bank = fd_banks_pool_ele( bank_pool, i );
232 96 : if( FD_UNLIKELY( !bank ) ) {
233 0 : FD_LOG_WARNING(( "Failed to get bank" ));
234 0 : return NULL;
235 0 : }
236 96 : bank->flags = 0UL;
237 96 : }
238 :
239 6 : fd_banks_set_bank_pool( banks, bank_pool );
240 :
241 : /* Now call _new() and _join for the cost tracker pool. Also, update
242 : the offset in the banks. */
243 :
244 6 : fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_cost_tracker_pool_join( fd_bank_cost_tracker_pool_new( cost_tracker_pool_mem, max_fork_width ) );
245 6 : FD_TEST( cost_tracker_pool );
246 6 : fd_banks_set_cost_tracker_pool( banks, cost_tracker_pool );
247 :
248 18 : for( ulong i=0UL; i<max_fork_width; i++ ) {
249 12 : fd_bank_cost_tracker_t * cost_tracker = fd_bank_cost_tracker_pool_ele( cost_tracker_pool, i );
250 12 : fd_cost_tracker_join( fd_cost_tracker_new( cost_tracker->data, 88888UL /* TODO: REAL SEED */ ) );
251 12 : }
252 :
253 : /* Now, call _new() and _join() for all of the CoW pools. */
254 6 : #define HAS_COW_1_LIMIT_1(name) \
255 24 : fd_rwlock_unwrite( &banks->name##_pool_lock ); \
256 24 : void * name##_mem = fd_bank_##name##_pool_new( name##_pool_mem, max_fork_width ); \
257 24 : if( FD_UNLIKELY( !name##_mem ) ) { \
258 0 : FD_LOG_WARNING(( "Failed to create " #name " pool" )); \
259 0 : return NULL; \
260 0 : } \
261 24 : fd_bank_##name##_t * name##_pool = fd_bank_##name##_pool_join( name##_pool_mem ); \
262 24 : if( FD_UNLIKELY( !name##_pool ) ) { \
263 0 : FD_LOG_WARNING(( "Failed to join " #name " pool" )); \
264 0 : return NULL; \
265 0 : } \
266 24 : fd_banks_set_##name##_pool( banks, name##_pool );
267 :
268 6 : #define HAS_COW_1_LIMIT_0(name) \
269 6 : fd_rwlock_unwrite( &banks->name##_pool_lock ); \
270 6 : void * name##_mem = fd_bank_##name##_pool_new( name##_pool_mem, max_total_banks ); \
271 6 : if( FD_UNLIKELY( !name##_mem ) ) { \
272 0 : FD_LOG_WARNING(( "Failed to create " #name " pool" )); \
273 0 : return NULL; \
274 0 : } \
275 6 : fd_bank_##name##_t * name##_pool = fd_bank_##name##_pool_join( name##_pool_mem ); \
276 6 : if( FD_UNLIKELY( !name##_pool ) ) { \
277 0 : FD_LOG_WARNING(( "Failed to join " #name " pool" )); \
278 0 : return NULL; \
279 0 : } \
280 6 : fd_banks_set_##name##_pool( banks, name##_pool );
281 :
282 : /* Do nothing for these. */
283 6 : #define HAS_COW_0_LIMIT_0(name)
284 :
285 6 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
286 30 : HAS_COW_##cow##_LIMIT_##limit_fork_width(name)
287 60 : FD_BANKS_ITER(X)
288 60 : #undef X
289 60 : #undef HAS_COW_0_LIMIT_0
290 60 : #undef HAS_COW_1_LIMIT_1
291 60 : #undef HAS_COW_1_LIMIT_0
292 :
293 : /* Now we need to assign offsets for all of the pools for each
294 : fd_bank_t. */
295 :
296 102 : for( ulong i=0UL; i<max_total_banks; i++ ) {
297 :
298 96 : fd_bank_t * bank = fd_banks_pool_ele( bank_pool, i );
299 96 : #define HAS_COW_1(name) \
300 480 : fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks ); \
301 480 : fd_bank_set_##name##_pool( bank, name##_pool ); \
302 480 : fd_bank_set_##name##_pool_lock( bank, &banks->name##_pool_lock );
303 96 : #define HAS_COW_0(name)
304 :
305 96 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
306 480 : HAS_COW_##cow(name)
307 480 : FD_BANKS_ITER(X)
308 96 : #undef X
309 96 : #undef HAS_COW_0
310 96 : #undef HAS_COW_1
311 :
312 : /* The cost tracker is not templatized and must be set manually. */
313 96 : fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks );
314 96 : fd_bank_set_cost_tracker_pool( bank, cost_tracker_pool );
315 96 : }
316 :
317 60 : banks->max_total_banks = max_total_banks;
318 60 : banks->max_fork_width = max_fork_width;
319 60 : banks->root_idx = ULONG_MAX;
320 :
321 60 : if( FD_UNLIKELY( !fd_stake_delegations_new( banks->stake_delegations_root, FD_RUNTIME_MAX_STAKE_ACCOUNTS, 0 ) ) ) {
322 0 : FD_LOG_WARNING(( "Unable to create stake delegations root" ));
323 0 : return NULL;
324 0 : }
325 :
326 6 : FD_COMPILER_MFENCE();
327 6 : FD_VOLATILE( banks->magic ) = FD_BANKS_MAGIC;
328 6 : FD_COMPILER_MFENCE();
329 :
330 6 : return shmem;
331 60 : }
332 :
333 : fd_banks_t *
334 12 : fd_banks_join( void * mem ) {
335 12 : fd_banks_t * banks = (fd_banks_t *)mem;
336 :
337 12 : if( FD_UNLIKELY( !banks ) ) {
338 0 : FD_LOG_WARNING(( "NULL banks" ));
339 0 : return NULL;
340 0 : }
341 :
342 12 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)banks, fd_banks_align() ) ) ) {
343 0 : FD_LOG_WARNING(( "misaligned banks" ));
344 0 : return NULL;
345 0 : }
346 :
347 12 : if( FD_UNLIKELY( banks->magic!=FD_BANKS_MAGIC ) ) {
348 3 : FD_LOG_WARNING(( "Invalid banks magic" ));
349 3 : return NULL;
350 3 : }
351 :
352 9 : FD_SCRATCH_ALLOC_INIT( l, banks );
353 9 : banks = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_align(), sizeof(fd_banks_t) );
354 9 : void * pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_banks_pool_align(), fd_banks_pool_footprint( banks->max_total_banks ) );
355 9 : void * cost_tracker_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_cost_tracker_pool_align(), fd_bank_cost_tracker_pool_footprint( banks->max_fork_width ) );
356 :
357 : /* Need to layout all of the CoW pools. */
358 0 : #define HAS_COW_1_LIMIT_1(name) \
359 36 : void * name##_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( banks->max_fork_width ) );
360 :
361 0 : #define HAS_COW_1_LIMIT_0(name) \
362 9 : void * name##_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_bank_##name##_pool_align(), fd_bank_##name##_pool_footprint( banks->max_total_banks ) );
363 :
364 : /* Don't need to layout if not CoW. */
365 0 : #define HAS_COW_0_LIMIT_0(name)
366 :
367 0 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
368 45 : HAS_COW_##cow##_LIMIT_##limit_fork_width(name)
369 45 : FD_BANKS_ITER(X)
370 0 : #undef X
371 0 : #undef HAS_COW_0_LIMIT_0
372 0 : #undef HAS_COW_1_LIMIT_0
373 0 : #undef HAS_COW_1_LIMIT_1
374 :
375 9 : FD_SCRATCH_ALLOC_FINI( l, fd_banks_align() );
376 :
377 45 : fd_bank_t * banks_pool = fd_banks_get_bank_pool( banks );
378 45 : if( FD_UNLIKELY( !banks_pool ) ) {
379 0 : FD_LOG_WARNING(( "Failed to join bank pool" ));
380 0 : return NULL;
381 0 : }
382 :
383 9 : if( FD_UNLIKELY( banks_pool!=fd_banks_pool_join( pool_mem ) ) ) {
384 0 : FD_LOG_WARNING(( "Failed to join bank pool" ));
385 0 : return NULL;
386 0 : }
387 :
388 9 : fd_bank_cost_tracker_t * cost_tracker_pool = fd_banks_get_cost_tracker_pool( banks );
389 9 : if( FD_UNLIKELY( !cost_tracker_pool ) ) {
390 0 : FD_LOG_WARNING(( "Failed to join cost tracker pool" ));
391 0 : return NULL;
392 0 : }
393 :
394 9 : if( FD_UNLIKELY( cost_tracker_pool!=fd_bank_cost_tracker_pool_join( cost_tracker_pool_mem ) ) ) {
395 0 : FD_LOG_WARNING(( "Failed to join cost tracker pool" ));
396 0 : return NULL;
397 0 : }
398 :
399 : /* Now, call _join() for all of the CoW pools. */
400 9 : #define HAS_COW_1(name) \
401 45 : fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks ); \
402 45 : if( FD_UNLIKELY( !name##_pool ) ) { \
403 0 : FD_LOG_WARNING(( "Failed to join " #name " pool" )); \
404 0 : return NULL; \
405 0 : } \
406 45 : if( FD_UNLIKELY( name##_pool!=fd_bank_##name##_pool_join( name##_pool_mem ) ) ) { \
407 0 : FD_LOG_WARNING(( "Failed to join " #name " pool" )); \
408 0 : return NULL; \
409 0 : }
410 :
411 : /* Do nothing when the field is not CoW. */
412 9 : #define HAS_COW_0(name)
413 :
414 9 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
415 45 : HAS_COW_##cow(name)
416 90 : FD_BANKS_ITER(X)
417 9 : #undef X
418 9 : #undef HAS_COW_0
419 9 : #undef HAS_COW_1
420 :
421 9 : return banks;
422 90 : }
423 :
424 : void *
425 9 : fd_banks_leave( fd_banks_t * banks ) {
426 :
427 9 : if( FD_UNLIKELY( !banks ) ) {
428 0 : FD_LOG_WARNING(( "NULL banks" ));
429 0 : return NULL;
430 0 : }
431 :
432 9 : return (void *)banks;
433 9 : }
434 :
435 : void *
436 3 : fd_banks_delete( void * shmem ) {
437 :
438 3 : if( FD_UNLIKELY( !shmem ) ) {
439 0 : FD_LOG_WARNING(( "NULL banks" ));
440 0 : return NULL;
441 0 : }
442 :
443 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned((ulong)shmem, fd_banks_align() ) ) ) {
444 0 : FD_LOG_WARNING(( "misaligned banks" ));
445 0 : return NULL;
446 0 : }
447 :
448 3 : fd_banks_t * banks = (fd_banks_t *)shmem;
449 3 : banks->magic = 0UL;
450 :
451 3 : return shmem;
452 3 : }
453 :
454 : fd_bank_t *
455 6 : fd_banks_init_bank( fd_banks_t * banks ) {
456 :
457 6 : if( FD_UNLIKELY( !banks ) ) {
458 0 : FD_LOG_WARNING(( "NULL banks" ));
459 0 : return NULL;
460 0 : }
461 :
462 6 : fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
463 :
464 6 : fd_rwlock_write( &banks->rwlock );
465 :
466 6 : if( FD_UNLIKELY( !fd_banks_pool_free( bank_pool ) ) ) {
467 0 : FD_LOG_WARNING(( "Failed to acquire bank" ));
468 0 : fd_rwlock_unwrite( &banks->rwlock );
469 0 : return NULL;
470 0 : }
471 6 : fd_bank_t * bank = fd_banks_pool_ele_acquire( bank_pool );
472 :
473 6 : #define HAS_COW_1(type, name, footprint) \
474 30 : bank->name##_dirty = 0; \
475 30 : bank->name##_pool_idx = fd_bank_##name##_pool_idx_null( fd_bank_get_##name##_pool( bank ) );
476 :
477 6 : #define HAS_COW_0(type, name, footprint) \
478 252 : fd_memset( bank->non_cow.name, 0, footprint );
479 :
480 6 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
481 282 : HAS_COW_##cow(type, name, footprint)
482 282 : FD_BANKS_ITER(X)
483 6 : #undef X
484 6 : #undef HAS_COW_0
485 6 : #undef HAS_COW_1
486 :
487 6 : ulong null_idx = fd_banks_pool_idx_null( bank_pool );
488 6 : bank->idx = fd_banks_pool_idx( bank_pool, bank );
489 6 : bank->next = null_idx;
490 6 : bank->parent_idx = null_idx;
491 6 : bank->child_idx = null_idx;
492 6 : bank->sibling_idx = null_idx;
493 :
494 : /* Set all CoW fields to null. */
495 6 : #define HAS_COW_1(name) \
496 30 : fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks ); \
497 30 : bank->name##_pool_idx = fd_bank_##name##_pool_idx_null( name##_pool ); \
498 30 : bank->name##_dirty = 0;
499 :
500 : /* Do nothing for these. */
501 6 : #define HAS_COW_0(name)
502 :
503 6 : #define HAS_LOCK_1(name) \
504 36 : fd_rwlock_unwrite(&bank->name##_lock);
505 6 : #define HAS_LOCK_0(name)
506 :
507 6 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
508 30 : HAS_COW_##cow(name); \
509 282 : HAS_LOCK_##has_lock(name)
510 282 : FD_BANKS_ITER(X)
511 6 : #undef X
512 6 : #undef HAS_COW_0
513 6 : #undef HAS_COW_1
514 6 : #undef HAS_LOCK_0
515 6 : #undef HAS_LOCK_1
516 :
517 6 : fd_bank_set_cost_tracker_pool( bank, fd_banks_get_cost_tracker_pool( banks ) );
518 6 : bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_null( fd_bank_get_cost_tracker_pool( bank ) );
519 6 : fd_rwlock_unwrite( &bank->cost_tracker_lock );
520 :
521 6 : bank->flags |= FD_BANK_FLAGS_INIT;
522 6 : bank->flags |= FD_BANK_FLAGS_FROZEN;
523 6 : bank->refcnt = 0UL;
524 :
525 6 : bank->first_fec_set_received_nanos = fd_log_wallclock();
526 6 : bank->first_transaction_scheduled_nanos = 0L;
527 6 : bank->last_transaction_finished_nanos = 0L;
528 :
529 : /* Now that the node is inserted, update the root */
530 :
531 6 : banks->root_idx = bank->idx;
532 :
533 6 : fd_rwlock_unwrite( &banks->rwlock );
534 6 : return bank;
535 6 : }
536 :
537 : fd_bank_t *
538 : fd_banks_clone_from_parent( fd_banks_t * banks,
539 : ulong child_bank_idx,
540 66 : ulong parent_bank_idx ) {
541 66 : fd_rwlock_write( &banks->rwlock );
542 :
543 66 : fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
544 66 : if( FD_UNLIKELY( !bank_pool ) ) {
545 0 : FD_LOG_CRIT(( "invariant violation: failed to get bank pool" ));
546 0 : }
547 :
548 : /* Make sure that the bank is valid. */
549 :
550 66 : fd_bank_t * child_bank = fd_banks_pool_ele( bank_pool, child_bank_idx );
551 66 : if( FD_UNLIKELY( !child_bank ) ) {
552 0 : FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu does not exist", child_bank_idx ));
553 0 : }
554 66 : if( FD_UNLIKELY( !(child_bank->flags&FD_BANK_FLAGS_INIT) ) ) {
555 0 : FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu is not initialized", child_bank_idx ));
556 0 : }
557 :
558 : /* Then make sure that the parent bank is valid and frozen. */
559 :
560 66 : fd_bank_t * parent_bank = fd_banks_pool_ele( bank_pool, parent_bank_idx );
561 66 : if( FD_UNLIKELY( !parent_bank ) ) {
562 0 : FD_LOG_CRIT(( "Invariant violation: parent bank for bank index %lu does not exist", parent_bank_idx ));
563 0 : }
564 66 : if( FD_UNLIKELY( !(parent_bank->flags&FD_BANK_FLAGS_FROZEN) ) ) {
565 0 : FD_LOG_CRIT(( "Invariant violation: parent bank for bank index %lu is not frozen", parent_bank_idx ));
566 0 : }
567 :
568 : /* We want to copy over the fields from the parent to the child,
569 : except for the fields which correspond to the header of the bank
570 : struct which either are used for internal memory managment or are
571 : fields which are not copied over from the parent bank (e.g. stake
572 : delegations delta and the cost tracker). We can take advantage of
573 : the fact that those fields are laid out at the top of the bank
574 : struct. */
575 :
576 66 : fd_memcpy( &child_bank->non_cow, &parent_bank->non_cow, sizeof(child_bank->non_cow) );
577 :
578 66 : #define HAS_COW_1(type, name, footprint) \
579 330 : child_bank->name##_dirty = 0; \
580 330 : child_bank->name##_pool_idx = parent_bank->name##_pool_idx;
581 :
582 66 : #define HAS_COW_0(type, name, footprint)
583 :
584 66 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
585 330 : HAS_COW_##cow(type, name, footprint)
586 330 : FD_BANKS_ITER(X)
587 66 : #undef X
588 66 : #undef HAS_COW_0
589 66 : #undef HAS_COW_1
590 :
591 : /* Initialization for the non-templatized fields. */
592 :
593 : /* The cost tracker pool needs to be set for the child bank and then
594 : a cost tracker pool element needs to be acquired.*/
595 :
596 66 : fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_get_cost_tracker_pool( child_bank );
597 66 : if( FD_UNLIKELY( fd_bank_cost_tracker_pool_free( cost_tracker_pool )==0UL ) ) {
598 0 : FD_LOG_CRIT(( "invariant violation: no free cost tracker pool elements" ));
599 0 : }
600 :
601 66 : child_bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_acquire( cost_tracker_pool );
602 66 : fd_rwlock_unwrite( &child_bank->cost_tracker_lock );
603 :
604 66 : child_bank->stake_delegations_delta_dirty = 0;
605 66 : fd_rwlock_unwrite( &child_bank->stake_delegations_delta_lock );
606 :
607 : /* Setup locks for new bank as free. */
608 66 : #define HAS_LOCK_1(name) \
609 396 : fd_rwlock_unwrite(&child_bank->name##_lock);
610 66 : #define HAS_LOCK_0(name)
611 :
612 66 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
613 396 : HAS_LOCK_##has_lock(name)
614 396 : FD_BANKS_ITER(X)
615 66 : #undef X
616 66 : #undef HAS_LOCK_0
617 66 : #undef HAS_LOCK_1
618 :
619 : /* If the parent bank is dead, then we also need to mark the child
620 : bank as being a dead block. */
621 66 : if( FD_UNLIKELY( parent_bank->flags & FD_BANK_FLAGS_DEAD ) ) {
622 0 : child_bank->flags |= FD_BANK_FLAGS_DEAD;
623 0 : }
624 :
625 66 : child_bank->refcnt = 0UL;
626 :
627 : /* Now the child bank is replayable. */
628 66 : child_bank->flags |= FD_BANK_FLAGS_REPLAYABLE;
629 :
630 66 : fd_rwlock_unwrite( &banks->rwlock );
631 :
632 66 : return child_bank;
633 66 : }
634 :
635 : /* Apply a fd_stake_delegations_t into the root. This assumes that there
636 : are no in-between, un-applied banks between the root and the bank
637 : being applied. This also assumes that the stake delegation object
638 : that is being applied is a delta. */
639 :
640 : static inline void
641 : fd_banks_stake_delegations_apply_delta( fd_bank_t * bank,
642 105 : fd_stake_delegations_t * stake_delegations_base ) {
643 :
644 105 : if( !bank->stake_delegations_delta_dirty ) {
645 45 : return;
646 45 : }
647 :
648 60 : fd_stake_delegations_t * stake_delegations_delta = fd_stake_delegations_join( bank->stake_delegations_delta );
649 60 : if( FD_UNLIKELY( !stake_delegations_delta ) ) {
650 0 : FD_LOG_CRIT(( "Failed to join stake delegations delta" ));
651 0 : }
652 :
653 60 : fd_stake_delegations_iter_t iter_[1];
654 60 : for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations_delta );
655 147 : !fd_stake_delegations_iter_done( iter );
656 87 : fd_stake_delegations_iter_next( iter ) ) {
657 87 : fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
658 87 : if( FD_LIKELY( !stake_delegation->is_tombstone ) ) {
659 75 : fd_stake_delegations_update(
660 75 : stake_delegations_base,
661 75 : &stake_delegation->stake_account,
662 75 : &stake_delegation->vote_account,
663 75 : stake_delegation->stake,
664 75 : stake_delegation->activation_epoch,
665 75 : stake_delegation->deactivation_epoch,
666 75 : stake_delegation->credits_observed,
667 75 : stake_delegation->warmup_cooldown_rate
668 75 : );
669 75 : } else {
670 12 : fd_stake_delegations_remove( stake_delegations_base, &stake_delegation->stake_account );
671 12 : }
672 87 : }
673 60 : }
674 :
675 : /* fd_bank_stake_delegation_apply_deltas applies all of the stake
676 : delegations for the entire direct ancestry from the bank to the
677 : root into a full fd_stake_delegations_t object. */
678 :
679 : static inline void
680 : fd_bank_stake_delegation_apply_deltas( fd_banks_t * banks,
681 : fd_bank_t * bank,
682 45 : fd_stake_delegations_t * stake_delegations ) {
683 :
684 : /* Naively what we want to do is iterate from the old root to the new
685 : root and apply the delta to the full state iteratively. */
686 :
687 : /* First, gather all of the pool indicies that we want to apply deltas
688 : for in reverse order starting from the new root. We want to exclude
689 : the old root since its delta has been applied previously. */
690 45 : ulong pool_indicies[ banks->max_total_banks ];
691 45 : ulong pool_indicies_len = 0UL;
692 :
693 45 : fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
694 :
695 45 : ulong curr_idx = fd_banks_pool_idx( bank_pool, bank );
696 150 : while( curr_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
697 105 : pool_indicies[pool_indicies_len++] = curr_idx;
698 105 : fd_bank_t * curr_bank = fd_banks_pool_ele( bank_pool, curr_idx );
699 105 : curr_idx = curr_bank->parent_idx;
700 105 : }
701 :
702 : /* We have populated all of the indicies that we need to apply deltas
703 : from in reverse order. */
704 :
705 150 : for( ulong i=pool_indicies_len; i>0; i-- ) {
706 105 : ulong idx = pool_indicies[i-1UL];
707 105 : fd_banks_stake_delegations_apply_delta( fd_banks_pool_ele( bank_pool, idx ), stake_delegations );
708 105 : }
709 45 : }
710 :
711 : fd_stake_delegations_t *
712 24 : fd_bank_stake_delegations_frontier_query( fd_banks_t * banks, fd_bank_t * bank ) {
713 :
714 24 : fd_rwlock_write( &banks->rwlock );
715 :
716 : /* First copy the rooted state into the frontier. */
717 24 : memcpy( banks->stake_delegations_frontier, banks->stake_delegations_root, FD_STAKE_DELEGATIONS_FOOTPRINT );
718 :
719 : /* Now apply all of the updates from the bank and all of its
720 : ancestors in order to the frontier. */
721 24 : fd_stake_delegations_t * stake_delegations = fd_stake_delegations_join( banks->stake_delegations_frontier );
722 24 : fd_bank_stake_delegation_apply_deltas( banks, bank, stake_delegations );
723 :
724 24 : fd_rwlock_unwrite( &banks->rwlock );
725 :
726 24 : return stake_delegations;
727 24 : }
728 :
729 : fd_stake_delegations_t *
730 3 : fd_banks_stake_delegations_root_query( fd_banks_t * banks ) {
731 3 : return fd_stake_delegations_join( banks->stake_delegations_root );
732 3 : }
733 :
734 : fd_bank_t const *
735 : fd_banks_advance_root( fd_banks_t * banks,
736 21 : ulong root_bank_idx ) {
737 :
738 21 : fd_rwlock_write( &banks->rwlock );
739 :
740 21 : fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
741 :
742 21 : ulong null_idx = fd_banks_pool_idx_null( bank_pool );
743 :
744 : /* We want to replace the old root with the new root. This means we
745 : have to remove banks that aren't descendants of the new root. */
746 :
747 21 : fd_bank_t const * old_root = fd_banks_root( banks );
748 21 : if( FD_UNLIKELY( !old_root ) ) {
749 0 : FD_LOG_CRIT(( "invariant violation: old root is NULL" ));
750 0 : }
751 :
752 21 : if( FD_UNLIKELY( old_root->refcnt!=0UL ) ) {
753 0 : FD_LOG_CRIT(( "refcnt for old root bank at index %lu is nonzero: %lu", old_root->idx, old_root->refcnt ));
754 0 : }
755 :
756 21 : fd_bank_t * new_root = fd_banks_bank_query( banks, root_bank_idx );
757 21 : if( FD_UNLIKELY( !new_root ) ) {
758 0 : FD_LOG_CRIT(( "invariant violation: new root is NULL" ));
759 0 : }
760 :
761 21 : if( FD_UNLIKELY( new_root->parent_idx!=old_root->idx ) ) {
762 0 : FD_LOG_CRIT(( "invariant violation: trying to advance root bank by more than one" ));
763 0 : }
764 :
765 21 : fd_stake_delegations_t * stake_delegations = fd_stake_delegations_join( banks->stake_delegations_root );
766 21 : fd_bank_stake_delegation_apply_deltas( banks, new_root, stake_delegations );
767 21 : new_root->stake_delegations_delta_dirty = 0;
768 :
769 : /* Now that the deltas have been applied, we can remove all nodes
770 : that are not direct descendants of the new root. */
771 21 : fd_bank_t * head = fd_banks_pool_ele( bank_pool, old_root->idx );
772 21 : head->next = fd_banks_pool_idx_null( bank_pool );
773 21 : fd_bank_t * tail = head;
774 :
775 75 : while( head ) {
776 54 : fd_bank_t * child = fd_banks_pool_ele( bank_pool, head->child_idx );
777 :
778 108 : while( FD_LIKELY( child ) ) {
779 :
780 54 : if( FD_LIKELY( child!=new_root ) ) {
781 33 : if( FD_UNLIKELY( child->refcnt!=0UL ) ) {
782 0 : FD_LOG_CRIT(( "refcnt for child bank at index %lu is %lu", child->idx, child->refcnt ));
783 0 : }
784 :
785 : /* Update tail pointers */
786 33 : tail->next = child->idx;
787 33 : tail = fd_banks_pool_ele( bank_pool, tail->next );
788 33 : tail->next = fd_banks_pool_idx_null( bank_pool );
789 :
790 33 : }
791 :
792 54 : child = fd_banks_pool_ele( bank_pool, child->sibling_idx );
793 54 : }
794 :
795 54 : fd_bank_t * next = fd_banks_pool_ele( bank_pool, head->next );
796 :
797 : /* Decide if we need to free any CoW fields. We free a CoW member
798 : from its pool if the dirty flag is set unless it is the same
799 : pool that the new root uses. */
800 54 : #define HAS_COW_1(name) \
801 540 : if( head->name##_dirty && head->name##_pool_idx!=new_root->name##_pool_idx ) { \
802 3 : fd_rwlock_write( &banks->name##_pool_lock ); \
803 3 : fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks ); \
804 3 : fd_bank_##name##_pool_idx_release( name##_pool, head->name##_pool_idx ); \
805 3 : fd_rwlock_unwrite( &banks->name##_pool_lock ); \
806 3 : }
807 : /* Do nothing for these. */
808 54 : #define HAS_COW_0(name)
809 :
810 54 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
811 270 : HAS_COW_##cow(name)
812 270 : FD_BANKS_ITER(X)
813 54 : #undef X
814 54 : #undef HAS_COW_0
815 54 : #undef HAS_COW_1
816 :
817 54 : head->flags = 0UL;
818 54 : fd_banks_pool_ele_release( bank_pool, head );
819 54 : head = next;
820 54 : }
821 :
822 : /* If the new root did not have the dirty bit set, that means the node
823 : didn't own the pool index. Change the ownership to the new root. */
824 21 : #define HAS_COW_1(name) \
825 105 : fd_bank_##name##_t * name##_pool = fd_banks_get_##name##_pool( banks ); \
826 105 : if( new_root->name##_pool_idx!=fd_bank_##name##_pool_idx_null( name##_pool ) ) { \
827 9 : new_root->name##_dirty = 1; \
828 9 : }
829 : /* Do nothing if not CoW. */
830 21 : #define HAS_COW_0(name)
831 :
832 21 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
833 105 : HAS_COW_##cow(name)
834 105 : FD_BANKS_ITER(X)
835 21 : #undef X
836 21 : #undef HAS_COW_0
837 21 : #undef HAS_COW_1
838 :
839 21 : new_root->parent_idx = null_idx;
840 21 : banks->root_idx = new_root->idx;
841 :
842 21 : fd_rwlock_unwrite( &banks->rwlock );
843 :
844 21 : return new_root;
845 21 : }
846 :
847 : void
848 3 : fd_banks_clear_bank( fd_banks_t * banks, fd_bank_t * bank ) {
849 :
850 3 : fd_rwlock_read( &banks->rwlock );
851 : /* Get the parent bank. */
852 3 : fd_bank_t * parent_bank = fd_banks_pool_ele( fd_banks_get_bank_pool( banks ), bank->parent_idx );
853 :
854 3 : fd_memset( &bank->non_cow, 0, sizeof(bank->non_cow) );
855 :
856 3 : #define HAS_COW_1(type, name, footprint) \
857 15 : fd_bank_##name##_t * name##_pool = fd_bank_get_##name##_pool( bank ); \
858 15 : if( bank->name##_dirty ) { \
859 : /* If the dirty flag is set, then we have a pool allocated for */ \
860 : /* this specific bank. We need to release the pool index and */ \
861 : /* assign the bank to the idx corresponding to the parent. */ \
862 6 : fd_bank_##name##_pool_idx_release( name##_pool, bank->name##_pool_idx ); \
863 6 : bank->name##_dirty = 0; \
864 6 : bank->name##_pool_idx = parent_bank ? parent_bank->name##_pool_idx : fd_bank_##name##_pool_idx_null( name##_pool ); \
865 6 : }
866 :
867 3 : #define HAS_COW_0(type, name, footprint)
868 :
869 3 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
870 15 : HAS_COW_##cow(type, name, footprint)
871 15 : FD_BANKS_ITER(X)
872 3 : #undef X
873 3 : #undef HAS_COW_0
874 3 : #undef HAS_COW_1
875 :
876 : /* We need to acquire a cost tracker element. */
877 3 : fd_bank_cost_tracker_t * cost_tracker_pool = fd_bank_get_cost_tracker_pool( bank );
878 3 : if( FD_UNLIKELY( bank->cost_tracker_pool_idx!=fd_bank_cost_tracker_pool_idx_null( cost_tracker_pool ) ) ) {
879 3 : fd_bank_cost_tracker_pool_idx_release( cost_tracker_pool, bank->cost_tracker_pool_idx );
880 3 : }
881 3 : bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_acquire( cost_tracker_pool );
882 3 : fd_rwlock_unwrite( &bank->cost_tracker_lock );
883 :
884 3 : bank->stake_delegations_delta_dirty = 0;
885 3 : fd_rwlock_unwrite( &bank->stake_delegations_delta_lock );
886 :
887 3 : fd_rwlock_unread( &banks->rwlock );
888 3 : }
889 :
890 : /* Is the fork tree starting at the given bank entirely eligible for
891 : pruning? Returns 1 for yes, 0 for no.
892 :
893 : See comment in fd_replay_tile.c for more details on safe pruning. */
894 : static int
895 27 : fd_banks_subtree_can_be_pruned( fd_bank_t * bank_pool, fd_bank_t * bank ) {
896 27 : if( FD_UNLIKELY( !bank ) ) {
897 0 : FD_LOG_CRIT(( "invariant violation: bank is NULL" ));
898 0 : }
899 :
900 27 : if( bank->refcnt!=0UL ) {
901 3 : return 0;
902 3 : }
903 :
904 : /* Recursively check all children. */
905 24 : ulong child_idx = bank->child_idx;
906 33 : while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
907 9 : fd_bank_t * child = fd_banks_pool_ele( bank_pool, child_idx );
908 9 : if( !fd_banks_subtree_can_be_pruned( bank_pool, child ) ) {
909 0 : return 0;
910 0 : }
911 9 : child_idx = child->sibling_idx;
912 9 : }
913 :
914 24 : return 1;
915 24 : }
916 :
917 : /* Mark everything in the fork tree starting at the given bank dead. */
918 :
919 : static void
920 27 : fd_banks_subtree_mark_dead( fd_bank_t * bank_pool, fd_bank_t * bank ) {
921 27 : if( FD_UNLIKELY( !bank ) ) {
922 0 : FD_LOG_CRIT(( "invariant violation: bank is NULL" ));
923 0 : }
924 27 : if( FD_UNLIKELY( bank->flags & FD_BANK_FLAGS_ROOTED ) ) {
925 0 : FD_LOG_CRIT(( "invariant violation: bank for idx %lu is rooted", bank->idx ));
926 0 : }
927 :
928 27 : bank->flags |= FD_BANK_FLAGS_DEAD;
929 :
930 : /* Recursively mark all children as dead. */
931 27 : ulong child_idx = bank->child_idx;
932 36 : while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
933 9 : fd_bank_t * child = fd_banks_pool_ele( bank_pool, child_idx );
934 9 : fd_banks_subtree_mark_dead( bank_pool, child );
935 9 : child_idx = child->sibling_idx;
936 9 : }
937 27 : }
938 :
939 : int
940 : fd_banks_advance_root_prepare( fd_banks_t * banks,
941 : ulong target_bank_idx,
942 15 : ulong * advanceable_bank_idx_out ) {
943 : /* TODO: An optimization here is to do a single traversal of the tree
944 : that would mark minority forks as dead while accumulating
945 : refcnts to determine which bank is the highest advanceable. */
946 :
947 15 : fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
948 15 : fd_rwlock_read( &banks->rwlock );
949 :
950 15 : fd_bank_t * root = fd_banks_root( banks );
951 15 : if( FD_UNLIKELY( !root ) ) {
952 0 : FD_LOG_WARNING(( "failed to get root bank" ));
953 0 : fd_rwlock_unread( &banks->rwlock );
954 0 : return 0;
955 0 : }
956 :
957 : /* Early exit if target is the same as the old root. */
958 15 : if( FD_UNLIKELY( root->idx==target_bank_idx ) ) {
959 0 : FD_LOG_WARNING(( "target bank_idx %lu is the same as the old root's bank index %lu", target_bank_idx, root->idx ));
960 0 : fd_rwlock_unread( &banks->rwlock );
961 0 : return 0;
962 0 : }
963 :
964 : /* Early exit if the root bank still has a reference to it, we can't
965 : advance from it unti it's released. */
966 15 : if( FD_UNLIKELY( root->refcnt!=0UL ) ) {
967 0 : fd_rwlock_unread( &banks->rwlock );
968 0 : return 0;
969 0 : }
970 :
971 15 : fd_bank_t * target_bank = fd_banks_pool_ele( bank_pool, target_bank_idx );
972 15 : if( FD_UNLIKELY( !target_bank ) ) {
973 0 : FD_LOG_CRIT(( "failed to get bank for valid pool idx %lu", target_bank_idx ));
974 0 : }
975 :
976 : /* Mark every node from the target bank up through its parents to the
977 : root as being rooted. We also need to figure out the oldest,
978 : non-rooted ancestor of the target bank since we only want to
979 : advance our root bank by one. */
980 15 : fd_bank_t * curr = target_bank;
981 15 : fd_bank_t * prev = NULL;
982 57 : while( curr && curr!=root ) {
983 42 : curr->flags |= FD_BANK_FLAGS_ROOTED;
984 42 : prev = curr;
985 42 : curr = fd_banks_pool_ele( bank_pool, curr->parent_idx );
986 42 : }
987 :
988 15 : ulong advance_candidate_idx = prev->idx;
989 :
990 : /* If we didn't reach the old root or there is no parent, target is
991 : not a descendant. */
992 15 : if( FD_UNLIKELY( !curr || prev->parent_idx!=root->idx ) ) {
993 0 : FD_LOG_CRIT(( "invariant violation: target bank_idx %lu is not a direct descendant of root bank_idx %lu %lu %lu", target_bank_idx, root->idx, prev->idx, prev->parent_idx ));
994 0 : }
995 :
996 : /* We should mark the old root bank as dead. */
997 15 : root->flags |= FD_BANK_FLAGS_DEAD;
998 :
999 : /* We will at most advance our root bank by one. This means we can
1000 : advance our root bank by one if each of the siblings of the
1001 : potential new root are eligible for pruning. Each of the sibling
1002 : subtrees can be pruned if the subtrees have no active references on
1003 : their bank. */
1004 15 : ulong child_idx = root->child_idx;
1005 42 : while( child_idx!=fd_banks_pool_idx_null( bank_pool ) ) {
1006 30 : fd_bank_t * child_bank = fd_banks_pool_ele( bank_pool, child_idx );
1007 30 : if( child_idx!=advance_candidate_idx ) {
1008 18 : fd_banks_subtree_mark_dead( bank_pool, child_bank );
1009 18 : if( !fd_banks_subtree_can_be_pruned( bank_pool, child_bank ) ) {
1010 3 : fd_rwlock_unread( &banks->rwlock );
1011 3 : return 0;
1012 3 : }
1013 18 : }
1014 27 : child_idx = child_bank->sibling_idx;
1015 27 : }
1016 :
1017 12 : *advanceable_bank_idx_out = advance_candidate_idx;
1018 12 : fd_rwlock_unread( &banks->rwlock );
1019 12 : return 1;
1020 15 : }
1021 :
1022 : fd_bank_t *
1023 : fd_banks_new_bank( fd_banks_t * banks,
1024 : ulong parent_bank_idx,
1025 66 : long now ) {
1026 :
1027 66 : fd_rwlock_write( &banks->rwlock );
1028 :
1029 66 : fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
1030 66 : if( FD_UNLIKELY( !bank_pool ) ) {
1031 0 : FD_LOG_CRIT(( "invariant violation: failed to get bank pool" ));
1032 0 : }
1033 :
1034 66 : if( FD_UNLIKELY( fd_banks_pool_free( bank_pool )==0UL ) ) {
1035 0 : FD_LOG_CRIT(( "invariant violation: no free bank indices available" ));
1036 0 : }
1037 :
1038 66 : ulong child_bank_idx = fd_banks_pool_idx_acquire( bank_pool );
1039 :
1040 : /* Make sure that the bank is valid. */
1041 :
1042 66 : fd_bank_t * child_bank = fd_banks_pool_ele( bank_pool, child_bank_idx );
1043 66 : if( FD_UNLIKELY( !child_bank ) ) {
1044 0 : FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu does not exist", child_bank_idx ));
1045 0 : }
1046 66 : if( FD_UNLIKELY( child_bank->flags&FD_BANK_FLAGS_INIT ) ) {
1047 0 : FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu is already initialized", child_bank_idx ));
1048 0 : }
1049 :
1050 66 : ulong null_idx = fd_banks_pool_idx_null( bank_pool );
1051 :
1052 66 : child_bank->idx = child_bank_idx;
1053 66 : child_bank->parent_idx = null_idx;
1054 66 : child_bank->child_idx = null_idx;
1055 66 : child_bank->sibling_idx = null_idx;
1056 66 : child_bank->next = null_idx;
1057 66 : child_bank->flags = FD_BANK_FLAGS_INIT;
1058 :
1059 : /* Then make sure that the parent bank is valid and frozen. */
1060 :
1061 66 : fd_bank_t * parent_bank = fd_banks_pool_ele( bank_pool, parent_bank_idx );
1062 66 : if( FD_UNLIKELY( !parent_bank ) ) {
1063 0 : FD_LOG_CRIT(( "Invariant violation: parent bank for bank index %lu does not exist", parent_bank_idx ));
1064 0 : }
1065 66 : if( FD_UNLIKELY( !(parent_bank->flags&FD_BANK_FLAGS_INIT) ) ) {
1066 0 : FD_LOG_CRIT(( "Invariant violation: bank for bank index %lu is already initialized", child_bank_idx ));
1067 0 : }
1068 :
1069 : /* Link node->parent */
1070 :
1071 66 : child_bank->parent_idx = parent_bank_idx;
1072 :
1073 : /* Link parent->node and sibling->node */
1074 :
1075 66 : if( FD_LIKELY( parent_bank->child_idx==null_idx ) ) {
1076 :
1077 : /* This is the first child so set as left-most child */
1078 :
1079 39 : parent_bank->child_idx = child_bank_idx;
1080 :
1081 39 : } else {
1082 : /* Already have children so iterate to right-most sibling. */
1083 :
1084 27 : fd_bank_t * curr_bank = fd_banks_pool_ele( bank_pool, parent_bank->child_idx );
1085 27 : if( FD_UNLIKELY( !curr_bank ) ) {
1086 0 : FD_LOG_CRIT(( "Invariant violation: child bank for bank index %lu does not exist", parent_bank->child_idx ));
1087 0 : }
1088 33 : while( curr_bank->sibling_idx != null_idx ) curr_bank = fd_banks_pool_ele( bank_pool, curr_bank->sibling_idx );
1089 :
1090 : /* Link to right-most sibling. */
1091 :
1092 27 : curr_bank->sibling_idx = child_bank_idx;
1093 27 : }
1094 :
1095 66 : child_bank->first_fec_set_received_nanos = now;
1096 66 : child_bank->first_transaction_scheduled_nanos = 0L;
1097 66 : child_bank->last_transaction_finished_nanos = 0L;
1098 :
1099 66 : fd_rwlock_unwrite( &banks->rwlock );
1100 66 : return child_bank;
1101 66 : }
1102 :
1103 : void
1104 : fd_banks_mark_bank_dead( fd_banks_t * banks,
1105 0 : fd_bank_t * bank ) {
1106 0 : fd_rwlock_write( &banks->rwlock );
1107 :
1108 0 : fd_banks_subtree_mark_dead( fd_banks_get_bank_pool( banks ), bank );
1109 :
1110 0 : fd_rwlock_unwrite( &banks->rwlock );
1111 0 : }
1112 :
1113 : void
1114 : fd_banks_mark_bank_frozen( fd_banks_t * banks,
1115 63 : fd_bank_t * bank ) {
1116 63 : if( FD_UNLIKELY( bank->flags&FD_BANK_FLAGS_FROZEN ) ) {
1117 0 : FD_LOG_CRIT(( "invariant violation: bank for idx %lu is already frozen", bank->idx ));
1118 0 : }
1119 :
1120 63 : fd_rwlock_write( &banks->rwlock );
1121 63 : bank->flags |= FD_BANK_FLAGS_FROZEN;
1122 :
1123 63 : if( FD_UNLIKELY( bank->cost_tracker_pool_idx==fd_bank_cost_tracker_pool_idx_null( fd_bank_get_cost_tracker_pool( bank ) ) ) ) {
1124 0 : FD_LOG_CRIT(( "invariant violation: cost tracker pool index is null" ));
1125 0 : }
1126 63 : fd_bank_cost_tracker_pool_idx_release( fd_bank_get_cost_tracker_pool( bank ), bank->cost_tracker_pool_idx );
1127 63 : bank->cost_tracker_pool_idx = fd_bank_cost_tracker_pool_idx_null( fd_bank_get_cost_tracker_pool( bank ) );
1128 63 : fd_rwlock_unwrite( &banks->rwlock );
1129 63 : }
1130 :
1131 : int
1132 0 : fd_banks_validate( fd_banks_t * banks ) {
1133 0 : fd_rwlock_read( &banks->rwlock );
1134 :
1135 0 : fd_bank_t * bank_pool = fd_banks_get_bank_pool( banks );
1136 :
1137 0 : FD_LOG_INFO(( "fd_banks_pool_free: %lu", fd_banks_pool_free( bank_pool ) ));
1138 :
1139 : /* First check that the number of elements acquired by the CoW pools
1140 : is not greater than the number of elements in the bank pool. */
1141 0 : #define HAS_COW_1(type, name, footprint) \
1142 0 : fd_bank_##name##_t * name##_pool = fd_bank_get_##name##_pool( bank ); \
1143 0 : if( fd_bank_##name##_pool_used( name##_pool ) > fd_bank_pool_used( bank_pool ) ) { \
1144 0 : FD_LOG_WARNING(( "Invariant violation: %s pool has more elements acquired than the bank pool %lu %lu", #name, fd_bank_##name##_pool_used( name##_pool ), fd_bank_pool_used( bank_pool ) )); \
1145 0 : fd_rwlock_unread( &banks->rwlock ); \
1146 0 : return 1; \
1147 0 : } \
1148 0 :
1149 0 : #define HAS_COW_0(type, name, footprint)
1150 :
1151 0 : #define X(type, name, footprint, align, cow, limit_fork_width, has_lock) \
1152 0 : HAS_COW_##cow(type, name, footprint) \
1153 0 : FD_BANKS_ITER(X)
1154 0 : #undef X
1155 0 : #undef HAS_COW_0
1156 0 : #undef HAS_COW_1
1157 0 : fd_rwlock_unread( &banks->rwlock );
1158 :
1159 0 : return 0;
1160 0 : }
|