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