LCOV - code coverage report
Current view: top level - flamenco/accdb - fd_accdb_private.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 51 82 62.2 %
Date: 2026-10-22 18:10:43 Functions: 5 90 5.6 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_accdb_fd_accdb_private_h
       2             : #define HEADER_fd_src_flamenco_accdb_fd_accdb_private_h
       3             : 
       4             : #include "fd_accdb_base.h"
       5             : #include "fd_accdb_shmem.h"
       6             : #include "fd_accdb_cache.h"
       7             : 
       8             : static inline void
       9           0 : spin_lock_acquire( int * lock ) {
      10           0 : # if FD_HAS_THREADS
      11           0 :   for(;;) {
      12           0 :     if( FD_LIKELY( !FD_ATOMIC_CAS( lock, 0, 1 ) ) ) break;
      13           0 :     FD_SPIN_PAUSE();
      14           0 :   }
      15             : # else
      16             :   *lock = 1;
      17             : # endif
      18           0 :   FD_COMPILER_MFENCE();
      19           0 : }
      20             : 
      21             : static inline void
      22           0 : spin_lock_release( int * lock ) {
      23           0 :   FD_COMPILER_MFENCE();
      24           0 : # if FD_HAS_THREADS
      25           0 :   FD_VOLATILE( *lock ) = 0;
      26             : # else
      27             :   *lock = 0;
      28             : # endif
      29           0 : }
      30             : 
      31             : struct fd_accdb_txn {
      32             :   union {
      33             :     struct { uint next; } pool;
      34             :     struct { uint next; } fork;
      35             :   };
      36             : 
      37             :   uint acc_pool_idx;
      38             : };
      39             : 
      40             : typedef struct fd_accdb_txn fd_accdb_txn_t;
      41             : 
      42             : #define POOL_NAME       txn_pool
      43     1116097 : #define POOL_ELE_T      fd_accdb_txn_t
      44      370178 : #define POOL_NEXT       pool.next
      45             : #define POOL_IDX_T      uint
      46     2232265 : #define POOL_IDX_WIDTH  32
      47             : #define POOL_IMPL_STYLE 0
      48             : #define POOL_LAZY       1
      49             : 
      50             : #include "../../util/tmpl/fd_pool_para.c"
      51             : 
      52             : #define SET_NAME       descends_set
      53             : #define SET_IMPL_STYLE 1
      54             : #include "../../util/tmpl/fd_set_dynamic.c"
      55             : 
      56             : struct fd_accdb_fork_shmem {
      57             :   uint generation;
      58             : 
      59             :   fd_accdb_fork_id_t parent_id;
      60             :   fd_accdb_fork_id_t child_id;
      61             :   fd_accdb_fork_id_t sibling_id;
      62             : 
      63             :   struct {
      64             :     ulong next;
      65             :   } pool;
      66             : 
      67             :   uint txn_head;
      68             : };
      69             : 
      70             : typedef struct fd_accdb_fork_shmem fd_accdb_fork_shmem_t;
      71             : 
      72             : #define POOL_NAME       fork_pool
      73      192410 : #define POOL_ELE_T      fd_accdb_fork_shmem_t
      74      128192 : #define POOL_NEXT       pool.next
      75             : #define POOL_IDX_T      ulong
      76             : #define POOL_IMPL_STYLE 0
      77             : 
      78             : #include "../../util/tmpl/fd_pool_para.c"
      79             : 
      80             : struct fd_accdb_partition {
      81             :   ulong marked_compaction;
      82             :   ulong write_offset;
      83             :   ulong compaction_offset;
      84             : 
      85             :   ulong bytes_freed;
      86             : 
      87             :   uchar layer; /* compaction tier this partition belongs to. */
      88             : 
      89             :   ulong read_ops;
      90             :   ulong bytes_read;
      91             :   ulong write_ops;
      92             :   ulong bytes_written;
      93             : 
      94             :   /* Tickcount (fd_tickcount) of the partition's lifecycle events.  Set
      95             :      only at partition creation and again when the partition closes
      96             :      (i.e. the layer's write head rotates off it). */
      97             :   long created_ticks;
      98             :   long filled_ticks;
      99             : 
     100             :   /* Compaction lifecycle flags.  queued is set when the partition is
     101             :      pushed onto the compaction_dlist and cleared when it is popped.
     102             :      compacting_now is set by the compaction tile around the actual
     103             :      compaction work for this partition. */
     104             :   uchar queued;
     105             :   uchar compacting_now;
     106             : 
     107             :   /* Per-compaction-pass telemetry accumulators for the
     108             :      accdb_compaction_completed event.  Reset when a partition's
     109             :      compaction begins (compacting_now set in background_compact) and
     110             :      accumulated as records are scanned/relocated. */
     111             :   long  compaction_start_wallclock;    /* timestamp when this pass began */
     112             :   ulong compaction_accounts_relocated; /* live records moved this pass */
     113             :   ulong compaction_bytes_relocated;    /* bytes moved this pass */
     114             :   ulong compaction_dead_records;       /* records skipped (no live index entry) this pass */
     115             : 
     116             :   /* Epoch at which this partition was enqueued for compaction.  Set by
     117             :      fd_accdb_shmem_bytes_freed when the partition crosses the
     118             :      freed-bytes threshold.  The compaction tile will not begin reading
     119             :      from this partition until all joiners that were in an
     120             :      epoch-protected critical section at enqueue time have exited,
     121             :      ensuring any in-flight pwritev2 to this partition has completed. */
     122             :   ulong compaction_ready_epoch;
     123             : 
     124             :   /* Epoch at which this partition was enqueued for deferred freeing.
     125             :      Set by compaction when the partition finishes compaction, and
     126             :      checked by the reclamation scan to determine when it is safe to
     127             :      release the partition back to the pool. */
     128             :   ulong epoch_tag;
     129             : 
     130             :   ulong pool_next;
     131             : 
     132             :   ulong dlist_prev;
     133             :   ulong dlist_next;
     134             : };
     135             : 
     136             : typedef struct fd_accdb_partition fd_accdb_partition_t;
     137             : 
     138             : #define POOL_NAME       partition_pool
     139             : #define POOL_T          fd_accdb_partition_t
     140           0 : #define POOL_NEXT       pool_next
     141             : #define POOL_IDX_T      ulong
     142             : #define POOL_IMPL_STYLE 1
     143             : 
     144             : #include "../../util/tmpl/fd_pool.c"
     145             : 
     146             : #define DLIST_NAME       compaction_dlist
     147             : #define DLIST_ELE_T      fd_accdb_partition_t
     148           0 : #define DLIST_PREV       dlist_prev
     149           0 : #define DLIST_NEXT       dlist_next
     150             : #define DLIST_IMPL_STYLE 1
     151             : 
     152             : #include "../../util/tmpl/fd_dlist.c"
     153             : 
     154             : /* deferred_free_dlist reuses the same prev/next fields as
     155             :    compaction_dlist.  A partition is in at most one of the two lists at
     156             :    any time: it is popped from compaction_dlist before being pushed onto
     157             :    deferred_free_dlist. */
     158             : 
     159             : #define DLIST_NAME       deferred_free_dlist
     160             : #define DLIST_ELE_T      fd_accdb_partition_t
     161           0 : #define DLIST_PREV       dlist_prev
     162           0 : #define DLIST_NEXT       dlist_next
     163             : #define DLIST_IMPL_STYLE 1
     164             : 
     165             : #include "../../util/tmpl/fd_dlist.c"
     166             : 
     167             : struct fd_accdb_cache_key {
     168             :   uchar pubkey[ 32UL ];
     169             :   uint generation;
     170             : };
     171             : 
     172             : typedef struct fd_accdb_cache_key fd_accdb_cache_key_t;
     173             : 
     174             : struct __attribute__((aligned(64))) fd_accdb_accmeta {
     175             :   fd_accdb_cache_key_t key;
     176             : 
     177             :   struct {
     178             :     uint next;
     179             :   } map;
     180             : 
     181             :   union {
     182             :     struct {
     183             :       uint next;
     184             :     } pool;
     185             :     uint cache_idx;
     186             :   };
     187             : 
     188             :   uint   executable_size;
     189             : 
     190             :   ulong  lamports;
     191             : 
     192             :   /* Pack offset and fork_id together into a single ulong to pack the
     193             :      struct into a single 64 byte cache line.  This is a performance win
     194             :      of 2-3%. */
     195             :   ulong  offset_fork;
     196             : };
     197             : 
     198             : typedef struct fd_accdb_accmeta fd_accdb_accmeta_t;
     199             : 
     200             : FD_STATIC_ASSERT( alignof(fd_accdb_accmeta_t)==64, layout );
     201             : FD_STATIC_ASSERT( sizeof (fd_accdb_accmeta_t)==64, layout );
     202             : 
     203     3558971 : #define FD_ACCDB_OFF_BITS  48UL
     204     2953269 : #define FD_ACCDB_OFF_MASK  ((1UL<<FD_ACCDB_OFF_BITS)-1UL)       /* 0x0000_FFFF_FFFF_FFFF */
     205     1100446 : #define FD_ACCDB_OFF_INVAL FD_ACCDB_OFF_MASK                    /* sentinel: offset bits all-ones */
     206             : 
     207             : /* The `size` field in fd_accdb_disk_meta_t (named executable_size in
     208             :    fd_accdb_accmeta_t) packs five things into 32 bits:
     209             : 
     210             :      bit  31     executable flag                       (FD_ACCDB_SIZE_EXEC_BIT)
     211             :      bit  30     cache_valid flag, in-memory only      (FD_ACCDB_SIZE_CACHE_VALID_BIT)
     212             :      bit  29     cache_claim flag, in-memory only      (FD_ACCDB_SIZE_CACHE_CLAIM_BIT)
     213             :      bit  28     pd_write flag,    in-memory only      (FD_ACCDB_SIZE_PD_WRITE_BIT)
     214             :      bits 27..0  data length in bytes                  (FD_ACCDB_SIZE_MASK)
     215             : 
     216             :    The data length is therefore 28 bits, max 256 MiB, still well above
     217             :    FD_RUNTIME_ACC_SZ_MAX of 10 MiB (enforced by the static assert below).
     218             : 
     219             :    The three upper flag bits exist only in the in-memory index, never on
     220             :    disk:
     221             :      - cache_valid (bit 30): when set, cache_idx holds a valid
     222             :        (class, idx) pair; when clear, cache_idx must not be dereferenced
     223             :        (it may hold a snapshot slot number or garbage).
     224             :      - cache_claim (bit 29): a short-lived eviction/install lock taken
     225             :        via CAS while a writer mutates the cache_idx <-> cache-line
     226             :        binding, so concurrent readers and the evictor do not race.
     227             :      - pd_write (bit 28): set on a committed version whose write changed
     228             :        BPF upgradeable-loader deploy status this slot (Deploy/Upgrade/
     229             :        Extend/Close).  Meaningful only while the accmeta's key.generation
     230             :        equals a live fork's generation (i.e. the version was committed on
     231             :        that fork this slot); across snapshot/root boundaries the bit is
     232             :        dead by construction because the generation no longer matches any
     233             :        live fork.  Carried explicitly by the two commit sites in
     234             :        fd_accdb_release and nowhere else.
     235             : 
     236             :    The on-disk representation (written via SIZE_PACK / SIZE_DATA) carries
     237             :    no in-memory flag: persisted bytes are unchanged, and compaction's
     238             :    copy_file_range preserves the record headers verbatim without
     239             :    rewriting them. */
     240             : 
     241      273285 : #define FD_ACCDB_SIZE_EXEC_BIT        (1U<<31)
     242      368360 : #define FD_ACCDB_SIZE_CACHE_VALID_BIT (1U<<30)
     243      372141 : #define FD_ACCDB_SIZE_CACHE_CLAIM_BIT (1U<<29)
     244        4329 : #define FD_ACCDB_SIZE_PD_WRITE_BIT    (1U<<28)
     245      953989 : #define FD_ACCDB_SIZE_MASK            ((1U<<28)-1U)
     246      367593 : #define FD_ACCDB_SIZE_PACK(sz,exec)   ((uint)(sz) | ((exec) ? FD_ACCDB_SIZE_EXEC_BIT : 0U))
     247      953989 : #define FD_ACCDB_SIZE_DATA(packed)    ((packed) & FD_ACCDB_SIZE_MASK)
     248      178842 : #define FD_ACCDB_SIZE_EXEC(packed)    (!!((packed) & FD_ACCDB_SIZE_EXEC_BIT))
     249      368360 : #define FD_ACCDB_SIZE_CACHE_VALID(p)  (!!((p) & FD_ACCDB_SIZE_CACHE_VALID_BIT))
     250           0 : #define FD_ACCDB_SIZE_CACHE_CLAIM(p)  (!!((p) & FD_ACCDB_SIZE_CACHE_CLAIM_BIT))
     251          40 : #define FD_ACCDB_SIZE_PD_WRITE(p)     (!!((p) & FD_ACCDB_SIZE_PD_WRITE_BIT))
     252             : 
     253             : FD_STATIC_ASSERT( (10UL<<20) < (1UL<<28), pd_write_bit_collides_with_len );
     254             : 
     255             : static inline ulong
     256      368701 : fd_accdb_acc_offset( fd_accdb_accmeta_t const * acc ) {
     257      368701 :   return acc->offset_fork & FD_ACCDB_OFF_MASK;
     258      368701 : }
     259             : 
     260             : static inline ushort
     261      238768 : fd_accdb_acc_fork_id( fd_accdb_accmeta_t const * acc ) {
     262      238768 :   return (ushort)( acc->offset_fork >> FD_ACCDB_OFF_BITS );
     263      238768 : }
     264             : 
     265             : static inline ulong
     266             : fd_accdb_acc_pack_offset_fork( ulong  offset,
     267      366934 :                                ushort fork_id ) {
     268      366934 :   return ( (ulong)fork_id << FD_ACCDB_OFF_BITS ) | ( offset & FD_ACCDB_OFF_MASK );
     269      366934 : }
     270             : 
     271             : /* fd_accdb_acc_xchg_offset atomically replaces the 48-bit offset
     272             :    portion of acc->offset_fork with new_offset while preserving the
     273             :    16-bit fork_id, and returns the previous 48-bit offset.  Uses a
     274             :    CAS loop so that concurrent compaction CAS and release-overwrite
     275             :    exchanges serialize correctly. */
     276             : 
     277             : static inline ulong
     278             : fd_accdb_acc_xchg_offset( fd_accdb_accmeta_t * acc,
     279      372153 :                            ulong               new_offset ) {
     280      372153 :   for(;;) {
     281      372153 :     ulong old_packed = FD_VOLATILE_CONST( acc->offset_fork );
     282      372153 :     ulong new_packed = ( old_packed & ~FD_ACCDB_OFF_MASK ) | ( new_offset & FD_ACCDB_OFF_MASK );
     283      372153 :     if( FD_LIKELY( FD_ATOMIC_CAS( &acc->offset_fork, old_packed, new_packed )==old_packed ) )
     284      372882 :       return old_packed & FD_ACCDB_OFF_MASK;
     285 >1844*10^16 :     FD_SPIN_PAUSE();
     286 >1844*10^16 :   }
     287      372153 : }
     288             : 
     289             : /* Packing helpers for the embedded acc cache index.  3 bits class
     290             :    in bits 31-29, 29 bits line index.  INVAL is the sentinel for
     291             :    "no cached location known".  FD_ACCDB_CACHE_LINE_MAX (defined in
     292             :    fd_accdb_cache.h) is the exclusive upper bound on representable
     293             :    line indices; per-class slot counts must not exceed it, or cidx
     294             :    values would alias. */
     295             : 
     296     1283638 : #define FD_ACCDB_ACC_CIDX_IDX_MASK  ((uint)(FD_ACCDB_CACHE_LINE_MAX-1UL))
     297      736906 : #define FD_ACCDB_ACC_CIDX_INVAL     UINT_MAX
     298      367683 : #define FD_ACCDB_ACC_CIDX_PACK(c,i) ((uint)( ((uint)(c)<<FD_ACCDB_CACHE_LINE_BITS) | ((uint)(i) & FD_ACCDB_ACC_CIDX_IDX_MASK) ))
     299     1284251 : #define FD_ACCDB_ACC_CIDX_CLASS(ci) ((ulong)((uint)(ci) >> FD_ACCDB_CACHE_LINE_BITS))
     300      915955 : #define FD_ACCDB_ACC_CIDX_IDX(ci)   ((ulong)((uint)(ci) & FD_ACCDB_ACC_CIDX_IDX_MASK))
     301             : 
     302             : #define POOL_NAME       acc_pool
     303      739485 : #define POOL_ELE_T      fd_accdb_accmeta_t
     304      367209 : #define POOL_NEXT       pool.next
     305             : #define POOL_IDX_T      uint
     306     2245003 : #define POOL_IDX_WIDTH  32
     307             : #define POOL_IMPL_STYLE 0
     308             : #define POOL_LAZY       1
     309             : 
     310             : #include "../../util/tmpl/fd_pool_para.c"
     311             : 
     312             : struct fd_accdb_cache_line {
     313             :   fd_accdb_cache_key_t key;
     314             : 
     315             :   uint acc_idx;
     316             :   uint seq; /* incremented each time line is reused */
     317             : 
     318             :   uint  refcnt;
     319             :   uchar persisted;
     320             :   uchar referenced;
     321             : 
     322             :   uint next;
     323             : 
     324             :   uchar owner[ 32UL ];
     325             : };
     326             : 
     327             : typedef struct fd_accdb_cache_line fd_accdb_cache_line_t;
     328             : 
     329             : typedef struct __attribute__((aligned(64))) { ulong val; } accdb_offset_t;
     330             : 
     331             : /* Partition offsets are packed into accdb_offset_t as:
     332             :      bits 63..51: partition pool index
     333             :      bits 50..0 : byte offset within the partition */
     334             : 
     335          36 : #define FD_ACCDB_PARTITION_OFF_BITS 51UL
     336             : 
     337             : static FD_FN_CONST inline accdb_offset_t
     338             : accdb_offset( ulong partition_idx,
     339          36 :               ulong partition_offset ) {
     340          36 :   return (accdb_offset_t){ .val = (partition_idx<<FD_ACCDB_PARTITION_OFF_BITS) | partition_offset };
     341          36 : }
     342             : 
     343             : static FD_FN_PURE inline ulong
     344           0 : packed_partition_idx( accdb_offset_t const * offset ) {
     345           0 :   return offset->val>>FD_ACCDB_PARTITION_OFF_BITS;
     346           0 : }
     347             : 
     348             : static FD_FN_PURE inline ulong
     349           0 : packed_partition_offset( accdb_offset_t const * offset ) {
     350           0 :   return offset->val & ((1UL<<FD_ACCDB_PARTITION_OFF_BITS)-1UL);
     351           0 : }
     352             : 
     353             : static FD_FN_PURE inline ulong
     354             : packed_partition_file_offset( accdb_offset_t const * offset,
     355           0 :                               ulong                  partition_sz ) {
     356           0 :    return (packed_partition_idx( offset )*partition_sz + packed_partition_offset( offset ));
     357           0 : }
     358             : 
     359             : /* Maximum number of concurrent joiners (tiles) that can publish an
     360             :    epoch in the accdb.  Each joiner claims a slot in the shared epoch
     361             :    array during fd_accdb_new.  Must be less than or equal to 256 so
     362             :    refcnt in cache lines can safely track the number of threads
     363             :    referencing each cache line without overflow.  With a uint refcnt
     364             :    field, 256 joiners is well within range. */
     365        3084 : #define FD_ACCDB_MAX_JOINERS (256UL)
     366             : 
     367             : /* EVICT_SENTINEL: stored in refcnt to indicate a cache line is being
     368             :    claimed by an eviction scan.  Any thread seeing this value must treat
     369             :    the line as unavailable. */
     370             : #define FD_ACCDB_EVICT_SENTINEL UINT_MAX
     371             : 
     372             : struct fd_accdb_shmem_private {
     373             :   int partition_lock  __attribute__((aligned(64)));
     374             :   int grow_lock       __attribute__((aligned(64)));
     375             : 
     376             :   /* Set non-zero by the snapin tile while a snapshot is being loaded.
     377             :      Suppresses compaction enqueue so the compaction tile does not race
     378             :      with bulk snapshot writes; fd_accdb_snapshot_load_end performs a
     379             :      one-shot sweep to enqueue any partitions that crossed the
     380             :      fragmentation threshold during the load. */
     381             :   int snapshot_loading;
     382             : 
     383             :   /* Set at construction (fd_accdb_shmem_new) when this validator
     384             :      supports bundles.  A bundle coalesces up to
     385             :      FD_ACCDB_MAX_TXN_PER_ACQUIRE transactions into one acquire, so when
     386             :      set, fd_accdb_acquire_inner permits pubkeys_cnt up to
     387             :      FD_ACCDB_MAX_ACQUIRE_CNT instead of the single-transaction limit
     388             :      FD_ACCDB_MAX_TX_ACCOUNT_LOCKS. */
     389             :   int bundle_enabled;
     390             : 
     391             :   /* Per-class CLOCK sweep position.  Atomically incremented by
     392             :      eviction scans (modulo cache_class_max[c]).  Each element is on
     393             :      its own cacheline to avoid false sharing between classes. */
     394             :   struct __attribute__((aligned(64))) { ulong val; } clock_hand[ FD_ACCDB_CACHE_CLASS_CNT ];
     395             : 
     396             :   /* Per-class CAS free list (Treiber stack) for fully-freed cache
     397             :      lines.  ver_top packs a 32-bit ABA version counter in bits
     398             :      63..32 and a uint pool index in bits 31..0.  UINT_MAX in the
     399             :      low 32 bits means empty. */
     400             :   struct __attribute__((aligned(64))) { ulong ver_top; } cache_free[ FD_ACCDB_CACHE_CLASS_CNT ];
     401             : 
     402             :   /* Per-class approximate depth of the CAS free list.  Atomically
     403             :      incremented on push, decremented on pop.  Used by the
     404             :      background pre-eviction loop to decide when to refill. */
     405             :   struct __attribute__((aligned(64))) { ulong val; } cache_free_cnt[ FD_ACCDB_CACHE_CLASS_CNT ];
     406             : 
     407             :   fd_accdb_fork_id_t root_fork_id;
     408             : 
     409             :   ulong seed;
     410             : 
     411             :   /* generation is a monotonically increasing counter assigned to each
     412             :      fork on creation.  When a fork is rooted, its pool slot (fork_id)
     413             :      is freed and may be recycled by a new fork, making fork_id in
     414             :      on-disk metadata useless for identifying entries from that freed
     415             :      fork.  But generation persists in disk metadata and is never
     416             :      recycled.
     417             : 
     418             :      Any rooted fork is by definition an ancestor of all live forks, so
     419             :      entries with generation <= root_fork->generation are
     420             :      unconditionally visible without consulting descends_set.  For
     421             :      entries with generation > root_fork->generation, the fork_id is
     422             :      still valid and descends_set is used to check ancestry.
     423             : 
     424             :      KEY INVARIANT: descends_set is ONLY consulted when generation >
     425             :      root_generation, which means the fork_id has NOT been rooted yet
     426             :      and its pool slot is still live.  This is what makes it safe for
     427             :      fork_slot_defer to eagerly clear descends_set bits for retired
     428             :      forks: rooted fork bits are dead (bypassed by the generation fast
     429             :      path), and purged fork bits were already 0 in all live forks'
     430             :      descends_sets (a purged fork is never an ancestor of a live fork).
     431             :      */
     432             :   uint generation;
     433             : 
     434             :   /* Lazy initial-allocation counter per size class.  Atomically
     435             :      incremented by acquire_cache_line (with undo on overflow). Each
     436             :      element is on its own cacheline to avoid false sharing between
     437             :      classes. */
     438             :   struct __attribute__((aligned(64))) { ulong val; } cache_class_init[ FD_ACCDB_CACHE_CLASS_CNT ];
     439             : 
     440             :   ulong cache_class_max[ FD_ACCDB_CACHE_CLASS_CNT ];
     441             : 
     442             :   /* Byte offsets from shmem base to the per-class cache regions. Each
     443             :      region is cache_class_max[c] * fd_accdb_cache_slot_sz[c] bytes,
     444             :      with each slot holding an fd_accdb_cache_line_t header followed by
     445             :      up to (fd_accdb_cache_slot_sz[c] - META_SZ) bytes of account data.
     446             :      */
     447             :   ulong cache_region_off[ FD_ACCDB_CACHE_CLASS_CNT ];
     448             : 
     449             :   /* Background pre-eviction watermarks (computed once in shmem_new).
     450             :      cache_free_target[c]: desired free-list depth for class c.
     451             :      cache_free_low_water[c]: trigger threshold ((target*3)/4). */
     452             :   ulong cache_free_target   [ FD_ACCDB_CACHE_CLASS_CNT ];
     453             :   ulong cache_free_low_water[ FD_ACCDB_CACHE_CLASS_CNT ];
     454             : 
     455             :   /* cache_class_used[i].val holds the number of reserved cache
     456             :      slots in size class i.  Acquire atomically increments; if the
     457             :      result exceeds cache_class_max[i] the reservation overflowed
     458             :      and the thread subtracts back and retries.  Release atomically
     459             :      decrements.  Each element is on its own cacheline to avoid
     460             :      false sharing between classes.  Invariant:
     461             :        used[i].val + available[i] == cache_class_max[i]
     462             :      at all times. */
     463             :   struct __attribute__((aligned(64))) { ulong val; } cache_class_used[ FD_ACCDB_CACHE_CLASS_CNT ];
     464             : 
     465             :   /* Per-layer write heads.  whead[0] is the hot (execution) write
     466             :      head, updated with atomic fetch-and-add by acquire/release
     467             :      threads.  whead[1..N-1] are compaction write heads, each
     468             :      single-writer (compaction tile only). */
     469             :   accdb_offset_t whead[ FD_ACCDB_COMPACTION_LAYER_CNT ];
     470             :   int            has_partition[ FD_ACCDB_COMPACTION_LAYER_CNT ];
     471             : 
     472             :   ulong partition_cnt;
     473             :   ulong partition_sz;
     474             :   ulong partition_max;         /* acquired high water, for snapshot recovery */
     475             :   ulong partition_fallocated;  /* partitions backed by the file, can run ahead */
     476             : 
     477             :   ulong chain_cnt;
     478             :   ulong max_live_slots;
     479             :   ulong max_accounts;
     480             :   ulong max_account_writes_per_slot;
     481             : 
     482             :   /* Hard upper bound on concurrent joiners, set at construction.
     483             :      Used to determine whether cache_class_used tracking can be
     484             :      skipped for a given class (when max[c] >= MIN_RESERVED *
     485             :      joiner_cnt, every reservation succeeds trivially). */
     486             :   ulong joiner_cnt_max;
     487             : 
     488             :   /* Per-joiner worst-case cache reservation, set at construction.
     489             :      Used by fd_accdb_reset to replicate the cache_class_used sentinel
     490             :      logic from fd_accdb_shmem_new. */
     491             :   ulong cache_min_reserved;
     492             : 
     493             :   ulong partition_pool_off;
     494             : 
     495             :   /* compaction_dlist_off[k] is the byte offset (from shmem base) of
     496             :      the dlist sentinel for layer k.  Partitions at layer k that
     497             :      reach the freed-bytes threshold are enqueued here for compaction
     498             :      into layer k+1, or into layer k itself for the deepest layer. */
     499             :   ulong compaction_dlist_off[ FD_ACCDB_COMPACTION_LAYER_CNT ];
     500             : 
     501             :   /* Epoch-based safe reclamation for compacted partitions.
     502             : 
     503             :      epoch is a monotonically increasing counter incremented by the
     504             :      compaction tile each time a partition finishes compaction.  The
     505             :      completed partition is tagged with the current epoch and pushed
     506             :      onto a deferred-free list instead of being released immediately.
     507             : 
     508             :      joiner_epochs[i] holds the epoch observed by joiner i at the start
     509             :      of its epoch-protected critical section, or ULONG_MAX when idle.
     510             :      The compaction tile scans this array to find the minimum observed
     511             :      epoch; any deferred partition tagged with an epoch strictly less
     512             :      than that minimum is safe to release, because every epoch-protected
     513             :      operation that could have snapshotted an offset into that partition
     514             :      has since exited its critical section.
     515             : 
     516             :      joiner_cnt is claimed via atomic fetch-and-add in fd_accdb_new
     517             :      and never decremented. */
     518             :   ulong epoch __attribute__((aligned(64)));
     519             : 
     520             :   /* Synchronization with snapshot producer to inhibit compaction
     521             :      Holds one of FD_ACCDB_SNAPSHOT_SYNC_* */
     522             :   ulong snapshot_sync __attribute__((aligned(64)));
     523             : 
     524             :   /* Each joiner epoch is padded to a full cache line to prevent
     525             :      false sharing between joiners writing to adjacent slots. */
     526             :   struct __attribute__((aligned(64))) { ulong val; } joiner_epochs[ FD_ACCDB_MAX_JOINERS ];
     527             :   ulong joiner_cnt __attribute__((aligned(64)));
     528             :   ulong deferred_free_dlist_off;
     529             : 
     530             :   fd_accdb_shmem_metrics_t shmetrics[1];
     531             : 
     532             :   /* Command slot for T1 -> T2 offloading of advance_root / purge.
     533             :      Padded to its own cache line to avoid false sharing with the
     534             :      hot epoch / joiner_epochs fields above. */
     535             : 
     536      192243 : #define FD_ACCDB_CMD_IDLE            (0U)
     537           0 : #define FD_ACCDB_CMD_ADVANCE_ROOT    (1U)
     538      128097 : #define FD_ACCDB_CMD_PURGE           (2U)
     539           0 : #define FD_ACCDB_CMD_CLEAR_DEFERRED  (3U)
     540           0 : #define FD_ACCDB_CMD_DRAIN_DEFERRED  (4U)
     541             : 
     542             :   uint   cmd_op       __attribute__((aligned(64))); /* FD_ACCDB_CMD_* */
     543             :   ushort cmd_fork_id;                               /* argument       */
     544             : 
     545             :   /* T2-only side buffer for deferred acc unlinks.  Holds the indices of
     546             :      accs that have been CAS-unlinked from their map chains in the
     547             :      current advance_root or purge call but cannot yet have pool.next
     548             :      written: a concurrent cold_load_acc may stomp it via the cache_idx
     549             :      union alias.  After wait_for_epoch_drain the list is materialized
     550             :      into pool.next links and released via acc_pool_release_chain.
     551             : 
     552             :      T2 is the sole writer (advance_root and purge both run on T2 via
     553             :      the cmd offload above), so cnt is plain.  Capacity is
     554             :      txn_max = max_live_slots * max_account_writes_per_slot, the same
     555             :      bound used to size txn_pool.  Stored as a byte offset from shmem
     556             :      base. */
     557             : 
     558             :   ulong deferred_acc_buf_off;
     559             :   ulong deferred_acc_buf_cnt;
     560             :   ulong deferred_acc_buf_max;
     561             :   ulong deferred_acc_epoch;
     562             : 
     563             :   acc_pool_shmem_t  acc_pool [1];
     564             :   fork_pool_shmem_t fork_pool[1];
     565             :   txn_pool_shmem_t  txn_pool [1];
     566             : 
     567             :   /* Track accounts modified since full snapshot.
     568             : 
     569             :        ------------++++++++.......                       - pruned blocks
     570             :        ^           ^      ^      ^                       + active blocks
     571             :        full snap   root   head   incremental (future)    . future blocks
     572             : 
     573             :      The validator thus needs to track the addresses of all accounts
     574             :      that have changed since the last full snapshot.  accdb forks cannot
     575             :      be used for this as fork information at the full snapshot slot is
     576             :      discarded.
     577             : 
     578             :      accdb deltas track this missing information. */
     579             : 
     580             :   struct {
     581             :     ulong seed;
     582             :     ulong chain_off;
     583             :     uint  chain_cnt;  /* power of 2 */
     584             :     uint  chain_mask; /* chain_cnt-1, contiguous runs of one bits */
     585             :     ulong ele_off;
     586             :     ulong ele_max;
     587             :     ulong head; /* bump alloc head */
     588             :   } delta;
     589             : 
     590             :   ulong magic; /* ==FD_ACCDB_SHMEM_MAGIC */
     591             : };
     592             : 
     593             : #endif /* HEADER_fd_src_flamenco_accdb_fd_accdb_private_h */

Generated by: LCOV version 1.14