LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_bank.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 115 129 89.1 %
Date: 2025-08-05 05:04:49 Functions: 47 4440 1.1 %

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

Generated by: LCOV version 1.14