LCOV - code coverage report
Current view: top level - discof/backup - fd_backup_disk.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 104 104 100.0 %
Date: 2026-08-15 04:34:25 Functions: 4 8 50.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_discof_backup_fd_backup_disk_h
       2             : #define HEADER_fd_src_discof_backup_fd_backup_disk_h
       3             : 
       4             : /* fd_backup_disk.h does streaming parsing of accdb disk partitions.
       5             :    Publishes discovered accounts (by disk offset) onto mcache/dcache. */
       6             : 
       7             : #include "fd_backup.h"
       8             : #include "fd_backup_accidx.h"
       9             : #include "fd_backup_shmem.h"
      10             : 
      11             : /* FD_SNAPMK_PF_LEAD is how far ahead of the record cursor the parser
      12             :    prefetches disk bytes. */
      13             : 
      14           9 : #define FD_SNAPMK_PF_LEAD 4096UL
      15             : 
      16             : /* fd_snapmk_accparse_t does streaming zero-copy parsing of accdb
      17             :    partitions.  Ingests a stream of disk data (arbitrarily fragmented)
      18             :    and produces an account-aligned fragment stream. */
      19             : 
      20             : struct fd_snapmk_accparse {
      21             : 
      22             :   /* current input frag */
      23             :   uchar const * data;
      24             :   ulong         data_sz;
      25             :   ulong         src_gaddr;
      26             :   ulong         src_off;         /* accdb file offset of data */
      27             :   ulong         frag_base_gaddr; /* src_gaddr at start of current frag */
      28             :   uchar const * pf_cursor;       /* prefetch high-water mark within current frag */
      29             :   int           input_active;
      30             : 
      31             :   /* record being parsed */
      32             :   uint  meta_sz;      /* header bytes buffered so far, if torn */
      33             :   int   acc_active;
      34             :   uint  acc_off;      /* account data bytes consumed so far */
      35             :   uint  acc_sz;       /* account data byte count */
      36             :   uint  acc_snap_sz;  /* account byte count in snapshot format */
      37             :   uint  acc_idx;      /* index entry, UINT_MAX if not in the snapshot */
      38             :   uint  acc_keep;
      39             :   ulong acc_file_off; /* accdb file offset of the record header */
      40             : 
      41             :   /* output frag staged by fd_snapmk_accparse_publish, drained on its
      42             :      next call (the rest of the frag is derived from the fields above,
      43             :      which hold still until the record completes) */
      44             :   ulong pub_gaddr;
      45             :   uint  pub_sz;
      46             :   int   pub_pending;
      47             :   int   pub_som;
      48             :   int   pub_eom;
      49             : 
      50             :   fd_backup_accidx_t idx;
      51             :   visited_set_t *    visited_set;
      52             : 
      53             :   /* defrag buffer for torn disk partition data */
      54             :   union __attribute__((packed)) {
      55             :     uchar buf[ sizeof(fd_accdb_disk_meta_t) ];
      56             :     fd_accdb_disk_meta_t meta;
      57             :   };
      58             : 
      59             :   /* Whole records parsed out of the current frag, awaiting index
      60             :      lookup.  Staged one batch ahead of the batch being resolved so the
      61             :      chain head prefetches issued here land during that walk. */
      62             :   uint        ps_cnt;
      63             :   ulong       ps_chain_idx[ FD_BACKUP_DISK_PARA ];
      64             :   uint        ps_frag_off [ FD_BACKUP_DISK_PARA ];
      65             :   ulong       ps_file_off [ FD_BACKUP_DISK_PARA ];
      66             : };
      67             : 
      68             : typedef struct fd_snapmk_accparse fd_snapmk_accparse_t;
      69             : 
      70             : FD_PROTOTYPES_BEGIN
      71             : 
      72             : /* Implementation */
      73             : 
      74             : /* fd_snapmk_accparse_lookup resolves cnt parsed disk records against
      75             :    the accdb index.  chain_idx[ i ] is the acc_map chain that record i's
      76             :    address hashes to, file_off[ i ] its accdb file offset.  Sets
      77             :    acc_idx[ i ] to the index entry to copy into the snapshot, or
      78             :    UINT_MAX if the record should be skipped.
      79             : 
      80             :    A record is claimed by the index entry pointing at its exact file
      81             :    offset.  accdb file offsets are unique.  The record is skipped if
      82             :    no entry points at it (a superseded version still on disk), if the
      83             :    entry is unrooted or a tombstone, or if the account was already
      84             :    emitted from cache or from another partition. */
      85             : 
      86             : static inline void
      87             : fd_snapmk_accparse_lookup( fd_snapmk_accparse_t * parse,
      88             :                            ulong const *          chain_idx,
      89             :                            ulong const *          file_off,
      90             :                            uint *                 acc_idx,
      91         435 :                            ulong                  cnt ) {
      92         435 :   fd_backup_accidx_t *       idx      = &parse->idx;
      93         435 :   fd_accdb_accmeta_t const * acc_pool = idx->acc_pool;
      94             : 
      95         435 :   uint cur[ FD_BACKUP_DISK_PARA ]; /* chain node lane i sits on */
      96             : 
      97         435 :   FD_COMPILER_MFENCE();
      98         435 :   FD_VOLATILE( *idx->epoch_slot ) = FD_VOLATILE_CONST( *idx->epoch );
      99         435 :   FD_HW_MFENCE();
     100             : 
     101             :   /* Chain heads were prefetched when this batch was prestaged, a full
     102             :      batch resolve ago. */
     103        1635 :   for( ulong i=0UL; i<cnt; i++ ) {
     104        1200 :     acc_idx[ i ] = UINT_MAX;
     105        1200 :     cur    [ i ] = FD_VOLATILE_CONST( idx->acc_map[ chain_idx[ i ] ] );
     106        1200 :   }
     107             : 
     108         435 :   int any;
     109         714 :   do {
     110         714 :     any = 0;
     111        2955 :     for( ulong i=0UL; i<cnt; i++ ) {
     112        2241 :       uint ele = cur[ i ];
     113        2241 :       if( !fd_backup_accidx_valid( idx, ele ) ) continue; /* lane retired */
     114             : 
     115        1998 :       fd_accdb_accmeta_t const * m = &acc_pool[ ele ];
     116        1998 :       uint  next = FD_VOLATILE_CONST( m->map.next       );
     117        1998 :       uint  gen  = FD_VOLATILE_CONST( m->key.generation );
     118        1998 :       ulong off  = FD_VOLATILE_CONST( m->offset_fork    );
     119        1998 :       ulong lam  = FD_VOLATILE_CONST( m->lamports       );
     120             : 
     121        1998 :       int hit = fd_backup_accidx_rooted( idx, gen, lam )
     122        1998 :               & ( ( off & FD_ACCDB_OFF_MASK )==file_off[ i ] );
     123        1998 :       fd_uint_store_if( hit, &acc_idx[ i ], ele );
     124             : 
     125             :       /* Resolving hit and next in the same pass keeps the next node
     126             :          prefetch to the lanes that will actually use it. */
     127        1998 :       int cont = (!hit) & fd_backup_accidx_valid( idx, next );
     128        1998 :       cur[ i ] = fd_uint_if( cont, next, UINT_MAX );
     129        1998 :       any     |= cont;
     130        1998 :       __builtin_prefetch( &acc_pool[ next & (uint)(-cont) ], 0, 3 );
     131        1998 :     }
     132         714 :   } while( any );
     133             : 
     134         435 :   FD_COMPILER_MFENCE();
     135         435 :   FD_VOLATILE( *idx->epoch_slot ) = ULONG_MAX;
     136             : 
     137             :   /* Claim each account for this snapshot, dropping the records that
     138             :      lost the claim to an earlier sighting. */
     139        1635 :   for( ulong i=0UL; i<cnt; i++ ) {
     140        1200 :     uint ele = acc_idx[ i ];
     141        1200 :     if( FD_UNLIKELY( ele==UINT_MAX ) ) continue;
     142         756 :     if( FD_UNLIKELY( fd_backup_visited_test_and_set( parse->visited_set, (ulong)ele ) ) ) {
     143         246 :       acc_idx[ i ] = UINT_MAX;
     144         246 :     }
     145         756 :   }
     146         435 : }
     147             : 
     148             : /* fd_snapmk_accparse_keep returns 1 if the record whose header is in
     149             :    parse->meta should be copied into the snapshot being produced, 0
     150             :    otherwise, and sets parse->acc_idx to its index entry.  This is the
     151             :    fallback for records that straddle an input frag; whole records go
     152             :    through fd_snapmk_accparse_publish_batch below. */
     153             : 
     154             : static inline int
     155          12 : fd_snapmk_accparse_keep( fd_snapmk_accparse_t * parse ) {
     156          12 :   if( FD_UNLIKELY( parse->meta.generation>parse->idx.root_generation ) ) {
     157           3 :     parse->acc_idx = UINT_MAX;
     158           3 :     return 0;
     159           3 :   }
     160           9 :   ulong chain_idx = fd_backup_accidx_chain( &parse->idx, parse->meta.pubkey );
     161           9 :   fd_snapmk_accparse_lookup( parse, &chain_idx, &parse->acc_file_off, &parse->acc_idx, 1UL );
     162           9 :   return parse->acc_idx!=UINT_MAX;
     163          12 : }
     164             : 
     165             : /* fd_snapmk_accparse_prestage consumes up to FD_BACKUP_DISK_PARA whole
     166             :    records from the current frag into parse->ps_*, and prefetches the
     167             :    index lines their lookup will need.  Does nothing unless the prestage
     168             :    is empty and the parser sits on a clean record boundary. */
     169             : 
     170             : static inline void
     171           6 : fd_snapmk_accparse_prestage( fd_snapmk_accparse_t * parse ) {
     172           6 :   if( FD_UNLIKELY( parse->ps_cnt ) ) return;
     173           6 :   if( FD_UNLIKELY( parse->pub_pending || parse->acc_active || parse->meta_sz ) ) return;
     174             : 
     175           6 :   ulong const meta_sz = sizeof(fd_accdb_disk_meta_t);
     176             : 
     177           6 :   fd_backup_accidx_t const * idx      = &parse->idx;
     178           6 :   uint const *               acc_map  = idx->acc_map;
     179           6 :   fd_accdb_accmeta_t const * acc_pool = idx->acc_pool;
     180             : 
     181           6 :   ulong * chain_idx = parse->ps_chain_idx;
     182           6 :   ulong   n         = 0UL;
     183             : 
     184          15 :   while( n<FD_BACKUP_DISK_PARA ) {
     185          15 :     if( parse->data_sz < meta_sz ) break; /* partial meta straddles frag end */
     186             : 
     187             :     /* Prefetch a fixed window ahead of the record cursor.  pf_cursor is
     188             :        a per-frag high-water mark, so each line is prefetched once no
     189             :        matter how the records fall across the window. */
     190           9 :     uchar const * pf_lim = parse->data + fd_ulong_min( parse->data_sz, FD_SNAPMK_PF_LEAD );
     191           9 :     uchar const * pf     = fd_ptr_if( parse->pf_cursor>parse->data, parse->pf_cursor, parse->data );
     192          21 :     for( ; pf<pf_lim; pf+=64UL ) __builtin_prefetch( pf, 0, 2 );
     193           9 :     parse->pf_cursor = pf_lim;
     194             : 
     195           9 :     fd_accdb_disk_meta_t const * dm = (fd_accdb_disk_meta_t const *)parse->data;
     196           9 :     ulong data_len = (ulong)FD_ACCDB_SIZE_DATA( dm->size );
     197           9 :     ulong rec      = meta_sz + data_len;
     198           9 :     if( parse->data_sz < rec ) break;     /* account data straddles frag end */
     199             : 
     200           9 :     if( FD_LIKELY( dm->generation <= idx->root_generation ) ) {
     201           6 :       chain_idx[ n ] = fd_backup_accidx_chain( idx, dm->pubkey );
     202           6 :       __builtin_prefetch( &acc_map[ chain_idx[ n ] ], 0, 3 );
     203           6 :       parse->ps_frag_off[ n ] = (uint)( parse->src_gaddr - parse->frag_base_gaddr );
     204           6 :       parse->ps_file_off[ n ] = parse->src_off;
     205           6 :       n++;
     206           6 :     }
     207             : 
     208           9 :     parse->data      += rec;
     209           9 :     parse->data_sz   -= rec;
     210           9 :     parse->src_gaddr += rec;
     211           9 :     parse->src_off   += rec;
     212           9 :   }
     213             : 
     214             :   /* Head pass: acc_map lines were prefetched during the record walk
     215             :      above; prefetch the acc_pool[head] lines here. */
     216          12 :   for( ulong i=0UL; i<n; i++ ) {
     217           6 :     uint head = FD_VOLATILE_CONST( acc_map[ chain_idx[ i ] ] );
     218           6 :     int  live = fd_backup_accidx_valid( idx, head );
     219           6 :     __builtin_prefetch( &acc_pool[ head & (uint)(-live) ], 0, 3 );
     220           6 :   }
     221             : 
     222           6 :   parse->ps_cnt = (uint)n;
     223           6 : }
     224             : 
     225             : /* fd_snapmk_accparse_publish_batch fills batch with the next run of
     226             :    whole records from the current frag and returns how many.  Returns 0
     227             :    once the frag holds no more whole records, leaving any straddling
     228             :    remainder to fd_snapmk_accparse_publish.  Frag offsets in the batch
     229             :    are relative to parse->frag_base_gaddr. */
     230             : 
     231             : static inline ulong
     232             : fd_snapmk_accparse_publish_batch( fd_snapmk_accparse_t *       parse,
     233           3 :                                   fd_backup_disk_batch_msg_t * batch ) {
     234           3 :   fd_snapmk_accparse_prestage( parse );
     235             : 
     236           3 :   ulong n = (ulong)parse->ps_cnt;
     237           3 :   if( !n ) return 0UL;
     238             : 
     239             :   /* Move batch N out of the prestage buffer, then stage N+1 before
     240             :      resolving N so N+1's prefetches overlap N's chain walk. */
     241           3 :   ulong chain_idx[ FD_BACKUP_DISK_PARA ];
     242           3 :   ulong file_off [ FD_BACKUP_DISK_PARA ];
     243           3 :   memcpy( chain_idx,       parse->ps_chain_idx, n*sizeof(ulong)       );
     244           3 :   memcpy( file_off,        parse->ps_file_off,  n*sizeof(ulong)       );
     245           3 :   memcpy( batch->frag_off, parse->ps_frag_off,  n*sizeof(uint)        );
     246           3 :   parse->ps_cnt = 0U;
     247             : 
     248           3 :   fd_snapmk_accparse_prestage( parse );
     249             : 
     250           3 :   fd_snapmk_accparse_lookup( parse, chain_idx, file_off, batch->acc_idx, n );
     251         381 :   for( ulong i=n; i<FD_BACKUP_DISK_PARA; i++ ) batch->acc_idx[ i ] = UINT_MAX;
     252             : 
     253           3 :   return n;
     254           3 : }
     255             : 
     256             : FD_PROTOTYPES_END
     257             : 
     258             : #endif /* HEADER_fd_src_discof_backup_fd_backup_disk_h */

Generated by: LCOV version 1.14