LCOV - code coverage report
Current view: top level - discof/backup - fd_snapzp_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 574 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 40 0.0 %

          Line data    Source code
       1             : /* The snapzp tile gathers account data, compresses it, and writes it to
       2             :    disk.
       3             : 
       4             :    It has two modes:
       5             :    - cache: resolve account pointers from in-memory DB cache (fall back
       6             :             to disk on overrun)
       7             :    - disk:  recover accounts from streaming disk reads (from snaprd)
       8             : 
       9             :    Internally, snapzp does streaming compression from DRAM into tile
      10             :    scratch memory.  Whenever compression scratch buffer fills, it is
      11             :    prepended on-the-fly with a matching compressed TAR header.  Finally,
      12             :    everything is written to disk. */
      13             : 
      14             : #define _GNU_SOURCE
      15             : #define ZSTD_STATIC_LINKING_ONLY
      16             : #include <zstd.h>
      17             : #include "fd_backup.h"
      18             : #include "fd_backup_cache.h"
      19             : #include "fd_backup_shmem.h"
      20             : #include "../../disco/metrics/fd_metrics.h"
      21             : #include "../../disco/stem/fd_stem.h"
      22             : #include "../../disco/topo/fd_topo.h"
      23             : 
      24             : #include <time.h> /* CLOCK_REALTIME */
      25             : #include <linux/futex.h>
      26             : #include "generated/fd_snapzp_tile_seccomp.h"
      27             : #include "../../tango/fseq/fd_fseq.h"
      28             : #include <fcntl.h>
      29             : #include <unistd.h>
      30             : #include <errno.h>
      31             : 
      32             : /* Compression buffer params */
      33           0 : #define RAW_BUF_SZ    (32UL<<20) /* FIXME make this configurable */
      34             : #define COMP_BOUND    ZSTD_COMPRESSBOUND( RAW_BUF_SZ )
      35           0 : #define COMP_HEAD     522 /* 10 byte Zstandard uncompressed header + 512 byte plaintext tar header */
      36           0 : #define COMP_BUF_SZ   FD_ULONG_ALIGN_UP( COMP_HEAD+COMP_BOUND+8UL, 4096UL )
      37             : 
      38             : struct fd_snapzp {
      39             :   fd_backup_cache_t  acc_cache[1];
      40             : 
      41             :   fd_backup_overrun_t * overrun;
      42             :   fd_backup_stats_t *   stats;
      43             :   fd_accdb_t *       accdb;
      44             :   fd_accdb_fork_id_t fork_id;
      45             : 
      46             :   /* compression buffer */
      47             :   ZSTD_CCtx *    zst;
      48             :   ulong          zst_in_rec; /* ZSTD_CStreamInSize() */
      49             :   uchar *        raw;
      50             :   ZSTD_inBuffer  raw_buf;
      51             :   ZSTD_outBuffer comp_buf;
      52             : 
      53             :   ulong idle_cnt;
      54             : 
      55             :   ulong kind_id;   /* index of this tile kind */
      56             :   ulong frame_cnt; /* frames written by this tile */
      57             :   ulong snapshot_slot;
      58             :   ulong slot_lo;   /* lowest appendvec slot this archive may use */
      59             :   ulong snapshot_account_cnt;
      60             :   ulong snapshot_account_sz;
      61             :   ulong snapshot_tombstone_cnt;
      62             :   ulong snapshot_cached_account_cnt;
      63             :   ulong snapshot_disk_account_cnt;
      64             :   ulong snapshot_zstd_padding_sz;
      65             :   ulong snapshot_uncompressed_sz;
      66             :   ulong snapshot_compress_ticks0;
      67             :   ulong snapshot_io_blocked_ticks0;
      68             : 
      69             :   /* input link */
      70             :   void *  snapmk_zp_mem;
      71             :   ulong   snapmk_zp_chunk0;
      72             :   ulong   snapmk_zp_wmark;
      73             :   void *  snaprd_mem;
      74             :   ulong   snaprd_data0;
      75             :   ulong   snaprd_data1;
      76             : 
      77             :   /* snapshot files (all O_DIRECT write-only seekable) */
      78             :   int   snap_fd;      /* current snap being written */
      79             :   ulong snap_fd_cnt;  /* open snap fd count (see FD_SNAP_DIO_FD) */
      80             : 
      81             :   /* bump allocator for file offsets used to coordinate snapzp take
      82             :      turns to write to a file.  512 byte aligned for direct I/O. */
      83             :   ulong volatile * file_off;
      84             : 
      85             :   /* countdown allocator for appendvec slots, shared the same way */
      86             :   ulong volatile * appendvec_slot_ticket;
      87             : 
      88             :   struct {
      89             :     int         active;
      90             :     fd_pubkey_t pubkey;
      91             :     fd_pubkey_t owner;
      92             :     uint        size;
      93             :     uint        acc_idx;
      94             :     ulong       data_rem;
      95             :     ulong       data_pad;
      96             :   } disk;
      97             : 
      98             :   struct {
      99             :     ulong accounts_compressed;
     100             :     ulong cache_read_torn;
     101             :     ulong bytes_compressed;
     102             :     ulong bytes_written;
     103             :     ulong io_blocked_ticks;
     104             :     ulong compress_ticks;
     105             :   } metrics;
     106             : 
     107             :   __attribute__((aligned(4096))) uchar raw_buf1 [ RAW_BUF_SZ  ];
     108             :   __attribute__((aligned(4096))) uchar comp_buf1[ COMP_BUF_SZ ];
     109             : };
     110             : typedef struct fd_snapzp fd_snapzp_t;
     111             : 
     112             : 
     113             : FD_FN_CONST static inline ulong
     114           0 : scratch_align( void ) {
     115           0 :   return FD_SHMEM_HUGE_PAGE_SZ;
     116           0 : }
     117             : 
     118             : FD_FN_PURE static inline ulong
     119           0 : scratch_footprint( fd_topo_tile_t const * tile ) {
     120           0 :   (void)tile;
     121           0 :   ulong l = FD_LAYOUT_INIT;
     122           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_snapzp_t), sizeof(fd_snapzp_t) );
     123           0 :   l = FD_LAYOUT_APPEND( l, fd_accdb_align(),     fd_accdb_footprint( tile->snapzp.max_live_slots, 0 ) );
     124           0 :   l = FD_LAYOUT_APPEND( l, 32UL,                 ZSTD_estimateCStreamSize( FD_BACKUP_ZSTD_LEVEL ) );
     125           0 :   return FD_LAYOUT_FINI( l, FD_SHMEM_HUGE_PAGE_SZ );
     126           0 : }
     127             : 
     128             : static void
     129             : privileged_init( fd_topo_t const *      topo,
     130           0 :                  fd_topo_tile_t const * tile ) {
     131           0 :   (void)topo;
     132           0 :   ulong snap_fd_max = tile->snapzp.snap_fd_cnt;
     133           0 :   FD_CHECK_ERR( snap_fd_max>0UL && snap_fd_max<=FD_SNAP_MAX,
     134           0 :                 "invalid snap_fd_max" );
     135           0 :   for( uint i=0U; i<snap_fd_max; i++ ) {
     136           0 :     if( FD_UNLIKELY( -1==fcntl( FD_SNAP_DIO_FD( i ), F_GETFD ) ) )
     137           0 :       FD_LOG_ERR(( "fcntl(snapshot pool fd %d) failed (%i-%s), was the snapshot pool initialized on boot?",
     138           0 :                    FD_SNAP_DIO_FD( i ), errno, fd_io_strerror( errno ) ));
     139           0 :   }
     140           0 : }
     141             : 
     142             : static void
     143             : unprivileged_init( fd_topo_t const *      topo,
     144           0 :                    fd_topo_tile_t const * tile ) {
     145           0 :   FD_CHECK_ERR( tile->kind_id < SNAPZP_TILE_MAX, "too many snapzp tiles" );
     146             : 
     147           0 :   FD_SCRATCH_ALLOC_INIT( l, fd_topo_obj_laddr( topo, tile->tile_obj_id ) );
     148           0 :   fd_snapzp_t * ctx      = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapzp_t), sizeof(fd_snapzp_t) );
     149           0 :   void *        _accdb   = FD_SCRATCH_ALLOC_APPEND( l, fd_accdb_align(),     fd_accdb_footprint( tile->snapzp.max_live_slots, 0 ) );
     150           0 :   void *        _zstd    = FD_SCRATCH_ALLOC_APPEND( l, 32UL,                 ZSTD_estimateCStreamSize( FD_BACKUP_ZSTD_LEVEL ) );
     151           0 :   FD_SCRATCH_ALLOC_FINI( l, scratch_align() );
     152             : 
     153           0 :   memset( ctx, 0, sizeof(fd_snapzp_t) );  /* 64 MiB-ish memset */
     154           0 :   ctx->snap_fd_cnt = tile->snapzp.snap_fd_cnt;
     155           0 :   ctx->snap_fd     = -1;
     156             : 
     157           0 :   ctx->zst = ZSTD_initStaticCStream( _zstd, ZSTD_estimateCStreamSize( FD_BACKUP_ZSTD_LEVEL ) );
     158           0 :   FD_TEST( ctx->zst );
     159           0 :   ulong zst_err;
     160           0 :   zst_err = ZSTD_CCtx_setParameter( ctx->zst, ZSTD_c_compressionLevel, FD_BACKUP_ZSTD_LEVEL );
     161           0 :   if( FD_UNLIKELY( ZSTD_isError( zst_err ) ) ) {
     162           0 :     FD_LOG_ERR(( "ZSTD_CCtx_setParameter(ZSTD_c_compressionLevel) failed: %s", ZSTD_getErrorName( zst_err ) ));
     163           0 :   }
     164           0 :   zst_err = ZSTD_CCtx_setParameter( ctx->zst, ZSTD_c_stableInBuffer, 1 );
     165           0 :   if( FD_UNLIKELY( ZSTD_isError( zst_err ) ) ) {
     166           0 :     FD_LOG_ERR(( "ZSTD_CCtx_setParameter(ZSTD_c_stableInBuffer=1) failed: %s", ZSTD_getErrorName( zst_err ) ));
     167           0 :   }
     168           0 :   zst_err = ZSTD_CCtx_setParameter( ctx->zst, ZSTD_c_stableOutBuffer, 1 );
     169           0 :   if( FD_UNLIKELY( ZSTD_isError( zst_err ) ) ) {
     170           0 :     FD_LOG_ERR(( "ZSTD_CCtx_setParameter(ZSTD_c_stableOutBuffer=1) failed: %s", ZSTD_getErrorName( zst_err ) ));
     171           0 :   }
     172           0 :   zst_err = ZSTD_CCtx_setParameter( ctx->zst, ZSTD_c_srcSizeHint, (int)RAW_BUF_SZ );
     173           0 :   if( FD_UNLIKELY( ZSTD_isError( zst_err ) ) ) {
     174           0 :     FD_LOG_ERR(( "ZSTD_CCtx_setParameter(ZSTD_c_srcSizeHint) failed: %s", ZSTD_getErrorName( zst_err ) ));
     175           0 :   }
     176           0 :   ctx->zst_in_rec = ZSTD_CStreamInSize();
     177           0 :   ctx->raw      = ctx->raw_buf1;
     178           0 :   ctx->raw_buf  = (ZSTD_inBuffer ){ .src = ctx->raw_buf1, .size = 0UL };
     179           0 :   ctx->comp_buf = (ZSTD_outBuffer){ .dst = ctx->comp_buf1+COMP_HEAD, .size = COMP_BUF_SZ-COMP_HEAD };
     180             : 
     181           0 :   for( ulong i=0UL; i<tile->in_cnt; i++ ) {
     182           0 :     fd_topo_link_t const * link = &topo->links[ tile->in_link_id[ i ] ];
     183           0 :     if( 0==strcmp( link->name, "snapmk_zp" ) ) {
     184           0 :       ctx->snapmk_zp_mem    = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp;
     185           0 :       ctx->snapmk_zp_chunk0 = fd_dcache_compact_chunk0( ctx->snapmk_zp_mem, link->dcache );
     186           0 :       ctx->snapmk_zp_wmark  = fd_dcache_compact_wmark ( ctx->snapmk_zp_mem, link->dcache, link->mtu );
     187           0 :     }
     188           0 :   }
     189           0 :   FD_TEST( ctx->snapmk_zp_mem );
     190             : 
     191             :   /* discover snaprd_out link */
     192           0 :   ulong snaprd_wksp_id = fd_topo_find_wksp( topo, "snaprd_out" );
     193           0 :   FD_CHECK_ERR( snaprd_wksp_id!=ULONG_MAX, "snapzp tile not joined to snaprd_out wksp" );
     194           0 :   ulong snaprd_link_id = fd_topo_find_link( topo, "snaprd_out", 0UL );
     195           0 :   FD_CHECK_ERR( snaprd_link_id!=ULONG_MAX, "snaprd_out:0: link not found" );
     196           0 :   ulong snaprd_dcache_id = topo->links[ snaprd_link_id ].dcache_obj_id;
     197           0 :   FD_CHECK_ERR( snaprd_dcache_id!=ULONG_MAX, "snaprd_out:0: dcache_obj_id not found" );
     198           0 :   FD_CHECK_ERR( topo->objs[ snaprd_dcache_id ].wksp_id==snaprd_wksp_id, "snaprd_out:0: dcache_obj_id not in snaprd_out wksp" );
     199           0 :   uchar const * dcache = topo->links[ snaprd_link_id ].dcache;
     200           0 :   FD_CHECK_ERR( dcache, "snaprd_out:0: dcache pointer is NULL" );
     201           0 :   ctx->snaprd_data0 = (ulong)dcache;
     202           0 :   ctx->snaprd_data1 = (ulong)dcache + fd_dcache_data_sz( dcache );
     203             : 
     204           0 :   ctx->snaprd_mem = topo->workspaces[ snaprd_wksp_id ].wksp;
     205           0 :   FD_TEST( ctx->snaprd_mem );
     206             : 
     207           0 :   ctx->kind_id = tile->kind_id;
     208           0 :   ctx->frame_cnt = 0UL;
     209           0 :   memset( &ctx->disk, 0, sizeof(ctx->disk) );
     210             : 
     211           0 :   void * _accdb_shmem = fd_topo_obj_laddr( topo, tile->snapzp.accdb_obj_id );
     212           0 :   fd_accdb_shmem_t * accdb_shmem_ro = fd_accdb_shmem_join( _accdb_shmem );
     213           0 :   FD_TEST( accdb_shmem_ro );
     214           0 :   ulong * epoch_fseq = fd_fseq_join( fd_topo_obj_laddr( topo, tile->snapzp.accdb_epoch_obj_id ) );
     215           0 :   FD_TEST( epoch_fseq );
     216           0 :   ctx->accdb = fd_accdb_join_readonly( _accdb, accdb_shmem_ro, epoch_fseq, FD_ACCDB_FD_RO );
     217           0 :   FD_TEST( ctx->accdb );
     218           0 :   FD_TEST( fd_backup_cache_join( ctx->acc_cache, accdb_shmem_ro, epoch_fseq ) );
     219           0 :   ctx->overrun = fd_backup_overrun( fd_topo_obj_laddr( topo, tile->snapzp.visited_set_obj_id ) );
     220           0 :   ctx->stats   = fd_backup_stats  ( fd_topo_obj_laddr( topo, tile->snapzp.visited_set_obj_id ) );
     221           0 :   FD_TEST( ctx->overrun );
     222           0 :   FD_TEST( ctx->stats   );
     223           0 :   FD_STATIC_ASSERT( SNAPZP_TILE_MAX==FD_BACKUP_STATS_MAX, backup_stats_max );
     224             : 
     225           0 :   ulong * zp_fseq = fd_fseq_join( fd_topo_obj_laddr( topo, tile->snapzp.zp_fseq_id ) ); FD_TEST( zp_fseq );
     226           0 :   ulong * zp_app  = fd_fseq_app_laddr( zp_fseq );
     227           0 :   ctx->file_off              = &zp_app[ 0 ];
     228           0 :   ctx->appendvec_slot_ticket = &zp_app[ 1 ];
     229           0 : }
     230             : 
     231             : static ulong
     232             : populate_allowed_fds( fd_topo_t const *      topo,
     233             :                       fd_topo_tile_t const * tile,
     234             :                       ulong                  out_fds_cnt,
     235           0 :                       int *                  out_fds ) {
     236           0 :   (void)topo;
     237           0 :   ulong snap_fd_cnt = tile->snapzp.snap_fd_cnt;
     238           0 :   FD_CHECK_ERR( out_fds_cnt>=3UL+snap_fd_cnt, "out_fds[] too small" );
     239           0 :   ulong out_cnt = 0UL;
     240           0 :   out_fds[ out_cnt++ ] = 2; /* stderr */
     241           0 :   if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
     242           0 :     out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
     243           0 :   out_fds[ out_cnt++ ] = FD_ACCDB_FD_RO;
     244           0 :   for( uint i=0U; i<snap_fd_cnt; i++ )
     245           0 :     out_fds[ out_cnt++ ] = FD_SNAP_DIO_FD( i );
     246           0 :   return out_cnt;
     247           0 : }
     248             : 
     249             : static ulong
     250             : populate_allowed_seccomp( fd_topo_t const *      topo,
     251             :                           fd_topo_tile_t const * tile,
     252             :                           ulong                  out_cnt,
     253           0 :                           struct sock_filter *   out ) {
     254           0 :   (void)topo;
     255           0 :   ulong snap_fd_cnt = tile->snapzp.snap_fd_cnt;
     256           0 :   populate_sock_filter_policy_fd_snapzp_tile(
     257           0 :       out_cnt, out,
     258           0 :       (uint)fd_log_private_logfile_fd(),
     259           0 :       (uint)FD_SNAP_DIO_FD( 0 ),
     260           0 :       (uint)FD_SNAP_DIO_FD( snap_fd_cnt-1U ),
     261           0 :       (uint)FD_ACCDB_FD_RO );
     262           0 :   return sock_filter_policy_fd_snapzp_tile_instr_cnt;
     263           0 : }
     264             : 
     265             : static void
     266             : before_credit( fd_snapzp_t *       ctx,
     267             :                fd_stem_context_t * stem,
     268           0 :                int *               charge_busy ) {
     269           0 :   (void)charge_busy;
     270           0 :   if( FD_LIKELY( ctx->snap_fd>=0 ) ) {
     271             :     /* Don't sleep while snapshot production is active
     272             :        FIXME this is quite wasteful */
     273           0 :     ctx->idle_cnt = 0UL;
     274           0 :     return;
     275           0 :   }
     276           0 :   if( FD_UNLIKELY( ctx->idle_cnt++ > 16384UL && !stem->sleep ) ) {
     277           0 :     fd_log_sleep( (long)1e6 );
     278           0 :   }
     279           0 : }
     280             : 
     281             : /* msg_start is called on all snapzp tiles before snapshot production
     282             :    starts. */
     283             : 
     284             : static void
     285             : msg_start( fd_snapzp_t *                 ctx,
     286           0 :            fd_backup_start_msg_t const * frag ) {
     287           0 :   FD_CHECK_CRIT( frag->snap_idx < ctx->snap_fd_cnt, "invalid snapshot pool slot" );
     288           0 :   ctx->snap_fd       = FD_SNAP_DIO_FD( frag->snap_idx );
     289           0 :   ctx->fork_id       = (fd_accdb_fork_id_t){ .val = frag->fork_id };
     290           0 :   ctx->snapshot_slot = frag->slot;
     291           0 :   ctx->slot_lo       = frag->slot_lo;
     292             : 
     293           0 :   ulong zst_err = ZSTD_CCtx_reset( ctx->zst, ZSTD_reset_session_only );
     294           0 :   if( FD_UNLIKELY( ZSTD_isError( zst_err ) ) ) {
     295           0 :     FD_LOG_ERR(( "ZSTD_CCtx_reset failed: %s", ZSTD_getErrorName( zst_err ) ));
     296           0 :   }
     297           0 :   ctx->frame_cnt = 0UL;
     298           0 :   ctx->snapshot_account_cnt = 0UL;
     299           0 :   ctx->snapshot_account_sz  = 0UL;
     300           0 :   ctx->snapshot_tombstone_cnt = 0UL;
     301           0 :   ctx->snapshot_cached_account_cnt = 0UL;
     302           0 :   ctx->snapshot_disk_account_cnt = 0UL;
     303           0 :   ctx->snapshot_zstd_padding_sz = 0UL;
     304           0 :   ctx->snapshot_uncompressed_sz = 0UL;
     305           0 :   ctx->snapshot_compress_ticks0 = ctx->metrics.compress_ticks;
     306           0 :   ctx->snapshot_io_blocked_ticks0 = ctx->metrics.io_blocked_ticks;
     307           0 :   fd_backup_worker_stats_t * stats = &ctx->stats->worker[ ctx->kind_id ];
     308           0 :   __atomic_store_n( &stats->account_cnt,         0UL, __ATOMIC_RELAXED );
     309           0 :   __atomic_store_n( &stats->account_sz,          0UL, __ATOMIC_RELAXED );
     310           0 :   __atomic_store_n( &stats->tombstone_cnt,       0UL, __ATOMIC_RELAXED );
     311           0 :   __atomic_store_n( &stats->cached_account_cnt,  0UL, __ATOMIC_RELAXED );
     312           0 :   __atomic_store_n( &stats->disk_account_cnt,    0UL, __ATOMIC_RELAXED );
     313           0 :   __atomic_store_n( &stats->zstd_data_frame_cnt, 0UL, __ATOMIC_RELAXED );
     314           0 :   __atomic_store_n( &stats->zstd_padding_sz,     0UL, __ATOMIC_RELAXED );
     315           0 :   __atomic_store_n( &stats->compress_ticks,      0UL, __ATOMIC_RELAXED );
     316           0 :   __atomic_store_n( &stats->io_blocked_ticks,    0UL, __ATOMIC_RELAXED );
     317           0 :   __atomic_store_n( &stats->uncompressed_sz,     0UL, __ATOMIC_RELEASE );
     318           0 :   memset( &ctx->disk, 0, sizeof(ctx->disk) );
     319           0 :   ctx->raw_buf.pos   = 0UL;
     320           0 :   ctx->raw_buf.size  = 0UL;
     321           0 :   ctx->comp_buf.pos  = 0UL;
     322           0 :   ctx->comp_buf.size = COMP_BUF_SZ-COMP_HEAD;
     323           0 : }
     324             : 
     325             : /* zip_work does opportunistic Zstandard compression work in memory.
     326             :    This function is tuned for a good tradeoff between latency
     327             :    (contiguous busy cycles) and throughput (dispatch overhead of libzstd
     328             :    function calls). */
     329             : 
     330             : static void
     331           0 : zip_work( fd_snapzp_t * ctx ) {
     332           0 :   if( FD_LIKELY( ctx->raw_buf.size - ctx->raw_buf.pos < ctx->zst_in_rec ) ) return;
     333           0 :   ulong raw_pos = ctx->raw_buf.pos;
     334           0 :   long  t0      = fd_tickcount();
     335           0 :   ulong ret = ZSTD_compressStream2( ctx->zst, &ctx->comp_buf, &ctx->raw_buf, ZSTD_e_continue );
     336           0 :   long  t1  = fd_tickcount();
     337           0 :   if( FD_UNLIKELY( ZSTD_isError( ret ) ) ) {
     338           0 :     FD_LOG_ERR(( "ZSTD_compressStream2(ZSTD_e_continue) failed: %s", ZSTD_getErrorName( ret ) ));
     339           0 :   }
     340           0 :   ctx->metrics.bytes_compressed += ctx->raw_buf.pos - raw_pos;
     341           0 :   ctx->metrics.compress_ticks   += (ulong)( t1-t0 );
     342           0 : }
     343             : 
     344             : /* zip_flush ends the current Zstandard compression frame and does a
     345             :    blocking direct I/O write.  Both uncompressed and compressed streams
     346             :    are padded up to 512 byte alignment to meet TAR and direct I/O
     347             :    requirements respectively. */
     348             : 
     349             : static void
     350           0 : zip_flush( fd_snapzp_t * ctx ) {
     351           0 :   FD_CHECK_CRIT( !ctx->disk.active, "attempted to flush with active defrag op" );
     352             : 
     353             :   /* Nothing buffered */
     354           0 :   if( FD_UNLIKELY( !ctx->raw_buf.size ) ) return;
     355             : 
     356             :   /* Align input frame by 512 bytes (TAR file format) */
     357           0 :   ulong content_usz = ctx->raw_buf.size;
     358           0 :   ulong content_asz = fd_ulong_align_up( content_usz, 512UL );
     359           0 :   if( content_asz > content_usz ) {
     360           0 :     FD_TEST( content_asz <= RAW_BUF_SZ );
     361           0 :     fd_memset( ctx->raw + content_usz, 0, content_asz - content_usz );
     362           0 :     ctx->raw_buf.size = content_asz;
     363           0 :   }
     364           0 :   ctx->snapshot_uncompressed_sz += sizeof(fd_tar_meta_t) + content_asz;
     365             : 
     366             :   /* Finish content compression frame */
     367           0 :   ulong raw_pos = ctx->raw_buf.pos;
     368           0 :   long  t0      = fd_tickcount();
     369           0 :   ulong ret = ZSTD_compressStream2( ctx->zst, &ctx->comp_buf, &ctx->raw_buf, ZSTD_e_end );
     370           0 :   long  t1  = fd_tickcount();
     371           0 :   if( FD_UNLIKELY( ZSTD_isError( ret ) ) ) {
     372           0 :     FD_LOG_ERR(( "ZSTD_compressStream2(ZSTD_e_end) failed: %s", ZSTD_getErrorName( ret ) ));
     373           0 :   }
     374           0 :   if( FD_UNLIKELY( ret!=0UL ) ) {
     375           0 :     FD_LOG_ERR(( "ZSTD_compressStream2(ZSTD_e_end) did not finish frame" ));
     376           0 :   }
     377           0 :   ctx->metrics.bytes_compressed += ctx->raw_buf.pos - raw_pos;
     378           0 :   ctx->metrics.compress_ticks   += (ulong)( t1-t0 );
     379           0 :   FD_TEST( ctx->raw_buf.pos == ctx->raw_buf.size );
     380           0 :   ctx->raw_buf.pos  = 0UL;
     381           0 :   ctx->raw_buf.size = 0UL;
     382             : 
     383             :   /* One appendvec per frame.  If, for some reason, we run out of slots
     384             :      to use, start incrementing the file index. */
     385           0 :   ulong appendvec_slot = __atomic_fetch_sub( ctx->appendvec_slot_ticket, 1UL, __ATOMIC_RELAXED );
     386           0 :   ulong appendvec_id   = 0UL;
     387           0 :   if( FD_UNLIKELY( appendvec_slot<ctx->slot_lo || appendvec_slot>ctx->snapshot_slot ) ) { /* out of slots (> means the counter wrapped below 0) */
     388           0 :     appendvec_id   = ctx->slot_lo - appendvec_slot; /* modular: 1, 2, ... */
     389           0 :     appendvec_slot = ctx->slot_lo;
     390           0 :   }
     391             : 
     392             :   /* Prepend compression frame with a TAR header
     393             :      (Zstandard frame with a 512 byte uncompressed block) */
     394           0 :   uchar * comp_head = (uchar *)ctx->comp_buf.dst - COMP_HEAD;
     395           0 :   memcpy( comp_head, (uchar[]){0x28,0xB5,0x2F,0xFD,0x60,0x00,0x01,0x01,0x10,0x00}, 10 );
     396           0 :   fd_tar_meta_t meta; fd_backup_tar_file_hdr( &meta, content_usz );
     397             : 
     398             :   /* Generate a unique file name */
     399           0 :   ctx->frame_cnt++;
     400           0 :   do {
     401           0 :     char * p = fd_cstr_init( meta.name );
     402           0 :     p = fd_cstr_append_cstr( p, "accounts/" );
     403           0 :     p = fd_cstr_append_ulong_as_text( p, 0, 0, appendvec_slot, fd_ulong_base10_dig_cnt( appendvec_slot ) );
     404           0 :     p = fd_cstr_append_char( p, '.' );
     405           0 :     p = fd_cstr_append_ulong_as_text( p, 0, 0, appendvec_id,   fd_ulong_base10_dig_cnt( appendvec_id   ) );
     406           0 :     fd_cstr_fini( p );
     407           0 :   } while(0);
     408           0 :   fd_tar_meta_set_chksum( &meta );
     409           0 :   memcpy( comp_head+10, &meta, sizeof(fd_tar_meta_t) );
     410             : 
     411             :   /* Align to block size with a skippable frame */
     412           0 :   ulong comp_usz = COMP_HEAD + ctx->comp_buf.pos;
     413           0 :   ulong comp_asz = fd_ulong_align_up( comp_usz, 4096UL );
     414           0 :   ulong pad_sz   = comp_asz - comp_usz;
     415           0 :   if( FD_UNLIKELY( pad_sz>0UL && pad_sz<8UL ) ) {
     416           0 :     comp_asz += 4096UL;
     417           0 :     pad_sz   += 4096UL;
     418           0 :   }
     419           0 :   FD_TEST( comp_asz <= COMP_BUF_SZ );
     420           0 :   if( FD_LIKELY( pad_sz>0UL ) ) {
     421           0 :     uchar * tail = (uchar *)ctx->comp_buf.dst + ctx->comp_buf.pos;
     422           0 :     FD_STORE( uint, tail,   ZSTD_MAGIC_SKIPPABLE_START );
     423           0 :     FD_STORE( uint, tail+4, (uint)( pad_sz-8 ) );
     424           0 :     fd_memset( tail+8, 0, pad_sz-8 );
     425           0 :     ctx->snapshot_zstd_padding_sz += pad_sz;
     426           0 :   }
     427             : 
     428             :   /* Allocate file range to write into */
     429           0 :   ulong off = __atomic_fetch_add( ctx->file_off, comp_asz, __ATOMIC_RELAXED );
     430           0 :   FD_TEST( fd_ulong_is_aligned( off,      4096UL ) );
     431           0 :   FD_TEST( fd_ulong_is_aligned( comp_asz, 4096UL ) );
     432           0 :   t0 = fd_tickcount();
     433           0 :   long write_sz = pwrite( ctx->snap_fd, comp_head, comp_asz, (long)off );
     434           0 :   t1 = fd_tickcount();
     435           0 :   if( FD_UNLIKELY( write_sz!=(long)comp_asz ) ) {
     436           0 :     FD_LOG_ERR(( "pwrite failed: %i-%s", errno, fd_io_strerror( errno ) ));
     437           0 :   }
     438           0 :   ctx->metrics.bytes_written    += comp_asz;
     439           0 :   ctx->metrics.io_blocked_ticks += (ulong)( t1-t0 );
     440             : 
     441             :   /* Free compressed buffer */
     442           0 :   ctx->comp_buf.pos = 0UL;
     443           0 : }
     444             : 
     445             : /* accmeta_await_evict waits until the account data belonging to the
     446             :    given index entry is evicted from cache to disk.  This is used to
     447             :    fallback to disk reads when a cache read is torn by eviction. */
     448             : 
     449             : static void
     450             : accmeta_await_evict( fd_snapzp_t * ctx,
     451           0 :                      uint          acc_idx ) {
     452           0 :   fd_backup_accidx_t * idx = &ctx->acc_cache->idx;
     453           0 :   FD_CHECK_CRIT( fd_backup_accidx_valid( idx, acc_idx ), "invalid account index" );
     454             : 
     455           0 :   fd_accdb_accmeta_t const * acc = &idx->acc_pool[ acc_idx ];
     456           0 :   for(;;) {
     457           0 :     FD_COMPILER_MFENCE();
     458           0 :     FD_VOLATILE( *idx->epoch_slot ) = FD_VOLATILE_CONST( *idx->epoch );
     459           0 :     FD_HW_MFENCE();
     460             : 
     461           0 :     ulong off_packed = FD_VOLATILE_CONST( acc->offset_fork );
     462           0 :     int   done       = ( off_packed & FD_ACCDB_OFF_MASK )!=FD_ACCDB_OFF_INVAL;
     463             : 
     464           0 :     FD_COMPILER_MFENCE();
     465           0 :     FD_VOLATILE( *idx->epoch_slot ) = ULONG_MAX;
     466             : 
     467           0 :     if( FD_LIKELY( done ) ) break;
     468           0 :     FD_SPIN_PAUSE(); /* FIXME yield to OS instead? */
     469           0 :   }
     470             : 
     471             :   /* tell snapmk that we were overrun */
     472           0 :   fd_backup_overrun_push( ctx->overrun, acc_idx );
     473           0 :   ctx->metrics.cache_read_torn++;
     474             : 
     475           0 :   FD_COMPILER_MFENCE();
     476           0 : }
     477             : 
     478             : /* msg_acc_cache instructs snapzp to gather, compress, and write out a
     479             :    bunch of accounts in cache.  Note that accdb cache entries are not
     480             :    pinned.  Account cache data can be evicted during read, leading to
     481             :    use-after-free, which this function gracefully recovers from. */
     482             : 
     483             : static void
     484             : msg_acc_cache( fd_snapzp_t *                 ctx,
     485           0 :                fd_backup_cache_msg_t const * batch ) {
     486           0 :   FD_CHECK_CRIT( ctx->snap_fd>=0, "invalid snapshot file descriptor" );
     487             : 
     488           0 :   fd_backup_accidx_t * idx      = &ctx->acc_cache->idx;
     489           0 :   int                  in_epoch = 0;
     490             : 
     491           0 :   ZSTD_inBuffer * buf = &ctx->raw_buf;
     492           0 :   for( ulong i=0UL; i<FD_BACKUP_CACHE_PARA; i++ ) {
     493           0 :     fd_pubkey_t const * pubkey  = &batch->pubkey [ i ];
     494           0 :     uint                acc_idx =  batch->acc_idx[ i ];
     495           0 :     if( acc_idx==UINT_MAX ) continue;
     496             : 
     497           0 :     if( FD_UNLIKELY( !in_epoch ) ) {
     498           0 :       FD_COMPILER_MFENCE();
     499           0 :       FD_VOLATILE( *idx->epoch_slot ) = FD_VOLATILE_CONST( *idx->epoch );
     500             :       /* a lock prefix is an expensive CPU pipeline hazard, therefore
     501             :          we hold onto our epoch for multiple accounts */
     502           0 :       FD_HW_MFENCE();
     503           0 :       in_epoch = 1;
     504           0 :     }
     505             : 
     506           0 :     ulong prev_sz = buf->size;
     507           0 :     int err = fd_backup_cache_read( ctx->acc_cache, pubkey, acc_idx, ctx->raw, &buf->size, RAW_BUF_SZ );
     508           0 :     if( FD_UNLIKELY( err==FD_BACKUP_CACHE_ERR_SPACE ) ) {
     509           0 :       FD_COMPILER_MFENCE();
     510           0 :       FD_VOLATILE( *idx->epoch_slot ) = ULONG_MAX;
     511           0 :       zip_flush( ctx );
     512           0 :       FD_COMPILER_MFENCE();
     513           0 :       FD_VOLATILE( *idx->epoch_slot ) = FD_VOLATILE_CONST( *idx->epoch );
     514           0 :       FD_HW_MFENCE();
     515           0 :       prev_sz = buf->size;
     516           0 :       err = fd_backup_cache_read( ctx->acc_cache, pubkey, acc_idx, ctx->raw, &buf->size, RAW_BUF_SZ );
     517           0 :       FD_CHECK_ERR( err!=FD_BACKUP_CACHE_ERR_SPACE, "Zstandard buffer too small" );
     518           0 :     }
     519             : 
     520           0 :     if( FD_UNLIKELY( err==FD_BACKUP_CACHE_ERR_MISS ) ) {
     521           0 :       FD_COMPILER_MFENCE();
     522           0 :       FD_VOLATILE( *idx->epoch_slot ) = ULONG_MAX;
     523           0 :       in_epoch = 0;
     524           0 :       accmeta_await_evict( ctx, acc_idx );
     525           0 :       continue;
     526           0 :     }
     527           0 :     FD_CHECK_ERR( err==FD_BACKUP_CACHE_SUCCESS, "unexpected cache error code" );
     528           0 :     ctx->snapshot_account_cnt++;
     529           0 :     ctx->snapshot_account_sz += buf->size - prev_sz;
     530           0 :     ctx->snapshot_cached_account_cnt++;
     531           0 :     ctx->metrics.accounts_compressed++;
     532           0 :   }
     533             : 
     534           0 :   if( FD_LIKELY( in_epoch ) ) {
     535           0 :     FD_COMPILER_MFENCE();
     536           0 :     FD_VOLATILE( *idx->epoch_slot ) = ULONG_MAX;
     537           0 :   }
     538           0 : }
     539             : 
     540             : static void
     541             : msg_acc_delta( fd_snapzp_t *                 ctx,
     542           0 :                fd_backup_delta_msg_t const * batch ) {
     543           0 :   FD_CHECK_CRIT( ctx->snap_fd>=0, "invalid snapshot file descriptor" );
     544           0 :   FD_CHECK_CRIT( !ctx->disk.active, "received account delta while already processing a disk account" );
     545           0 :   FD_CHECK_CRIT( batch->cnt<=FD_BACKUP_CACHE_PARA, "invalid delta account batch" );
     546             : 
     547           0 :   ulong const rec_max = sizeof(snap_acc_hdr_t) + FD_RUNTIME_ACC_SZ_MAX;
     548           0 :   FD_STATIC_ASSERT( sizeof(snap_acc_hdr_t)+FD_RUNTIME_ACC_SZ_MAX<=RAW_BUF_SZ, raw_buf_too_small );
     549             : 
     550           0 :   for( ulong i=0UL; i<(ulong)batch->cnt; i++ ) {
     551           0 :     if( FD_UNLIKELY( ctx->raw_buf.size + rec_max > RAW_BUF_SZ ) ) zip_flush( ctx );
     552             : 
     553           0 :     ulong            start = ctx->raw_buf.size;
     554           0 :     snap_acc_hdr_t * hdr   = (snap_acc_hdr_t *)( ctx->raw + start );
     555           0 :     memset( hdr, 0, sizeof(snap_acc_hdr_t) );
     556           0 :     hdr->pubkey = batch->pubkey[ i ];
     557             : 
     558           0 :     ulong lamports   = 0UL;
     559           0 :     ulong data_len   = 0UL;
     560           0 :     int   executable = 0;
     561           0 :     int source = fd_accdb_read_one_nocache( ctx->accdb, ctx->fork_id, batch->pubkey[ i ].uc,
     562           0 :                                             &lamports, &executable, hdr->owner.uc,
     563           0 :                                             ctx->raw + start + sizeof(snap_acc_hdr_t), &data_len );
     564           0 :     FD_CHECK_CRIT( data_len<=FD_RUNTIME_ACC_SZ_MAX, "accdb returned oversized account" );
     565           0 :     hdr->lamports   = lamports;
     566           0 :     hdr->executable = (uchar)!!executable;
     567           0 :     hdr->data_len   = data_len;
     568             : 
     569           0 :     ulong data_pad = fd_ulong_align_up( data_len, 8UL ) - data_len;
     570           0 :     if( data_pad ) fd_memset( ctx->raw + start + sizeof(snap_acc_hdr_t) + data_len, 0, data_pad );
     571           0 :     ctx->raw_buf.size = start + sizeof(snap_acc_hdr_t) + data_len + data_pad;
     572           0 :     ctx->snapshot_account_cnt++;
     573           0 :     ctx->snapshot_account_sz += sizeof(snap_acc_hdr_t) + data_len + data_pad;
     574           0 :     ctx->snapshot_tombstone_cnt      += source==FD_ACCDB_READ_ONE_NOCACHE_MISS;
     575           0 :     ctx->snapshot_cached_account_cnt += source==FD_ACCDB_READ_ONE_NOCACHE_CACHE;
     576           0 :     ctx->snapshot_disk_account_cnt   += source==FD_ACCDB_READ_ONE_NOCACHE_DISK;
     577           0 :     ctx->metrics.accounts_compressed++;
     578           0 :   }
     579           0 : }
     580             : 
     581             : /* accmeta_disk validates that an index entry (at acc_idx with given
     582             :    pubkey) is an on-disk rooted account.
     583             :    (Safe while compaction and rooting are disabled, as on-disk rooted
     584             :    accounts are pinned in the index and on-disk under these conditions.
     585             : 
     586             :    Returns a pointer to the index entry if a matching record is found,
     587             :    NULL otherwise.  (Never returns NULL under correct usage.) */
     588             : 
     589             : static fd_accdb_accmeta_t const *
     590             : accmeta_disk( fd_snapzp_t *       ctx,
     591             :               fd_pubkey_t const * pubkey,
     592             :               uint                size,
     593           0 :               uint                acc_idx ) {
     594           0 :   fd_backup_accidx_t const * idx = &ctx->acc_cache->idx;
     595           0 :   if( FD_UNLIKELY( !fd_backup_accidx_valid( idx, acc_idx ) ) ) return NULL;
     596             : 
     597           0 :   fd_accdb_accmeta_t const * acc = &idx->acc_pool[ acc_idx ];
     598           0 :   uint es = FD_VOLATILE_CONST( acc->executable_size );
     599           0 :   if( FD_UNLIKELY( FD_ACCDB_SIZE_DATA( es )!=FD_ACCDB_SIZE_DATA( size ) ) ) return NULL;
     600           0 :   if( FD_UNLIKELY( memcmp( acc->key.pubkey, pubkey->uc, sizeof(fd_pubkey_t) ) ) ) return NULL;
     601           0 :   return acc;
     602           0 : }
     603             : 
     604             : /* msg_acc_disk instructs snapzp to compress an account fragment that
     605             :    was read from the accdb disk file.  (slow path)
     606             : 
     607             :    The first fragment of an account has an associated backup_disk_msg
     608             :    descriptor (chunk serves as ptr, sz is the descriptor sz, i.e.
     609             :    sizeof(fd_backup_disk_msg_t)).  The first frag is indicated by the
     610             :    ctl.som flag.
     611             : 
     612             :    The remaining fragments do not have a descriptor.  The last frag is
     613             :    indicated by the ctl.eom frag.
     614             : 
     615             :    The account data fragment size is in tspub.
     616             : 
     617             :    msg_acc_disk_start is a helper function that starts a disk account
     618             :    defrag operation.  Returns the number of bytes consumed (always the
     619             :    frag data size). */
     620             : 
     621             : static ulong
     622             : msg_acc_disk_start( fd_snapzp_t *                ctx,
     623           0 :                     fd_backup_disk_msg_t const * frag ) {
     624           0 :   FD_CHECK_CRIT( ctx->snap_fd>=0, "invalid snapshot file descriptor" );
     625           0 :   FD_CHECK_CRIT( !ctx->disk.active, "received account SOM while already processing a disk account" );
     626             : 
     627           0 :   ulong data_len = (ulong)FD_ACCDB_SIZE_DATA( frag->size );
     628           0 :   ulong rec_sz   = sizeof(snap_acc_hdr_t) + fd_ulong_align_up( data_len, 8UL );
     629           0 :   FD_CHECK_CRIT( rec_sz<=RAW_BUF_SZ, "oversize snapshot account record" );
     630           0 :   FD_CHECK_CRIT( frag->snap_sz==rec_sz, "disk account snapshot size mismatch" );
     631           0 :   if( FD_UNLIKELY( ctx->raw_buf.size + rec_sz > RAW_BUF_SZ ) ) {
     632           0 :     zip_flush( ctx );
     633           0 :   }
     634             : 
     635           0 :   memset( &ctx->disk, 0, sizeof(ctx->disk) );
     636           0 :   ctx->disk.active  = 1;
     637           0 :   ctx->disk.pubkey  = frag->pubkey;
     638           0 :   ctx->disk.owner   = frag->owner;
     639           0 :   ctx->disk.size    = frag->size;
     640           0 :   ctx->disk.acc_idx = frag->acc_idx;
     641             : 
     642           0 :   fd_accdb_accmeta_t const * accmeta = accmeta_disk( ctx, &ctx->disk.pubkey, ctx->disk.size, ctx->disk.acc_idx );
     643           0 :   FD_CHECK_CRIT( accmeta, "bug in snapshot producer: rooted account disappeared from index" );
     644             : 
     645           0 :   snap_acc_hdr_t * hdr = (snap_acc_hdr_t *)( ctx->raw + ctx->raw_buf.size );
     646           0 :   memset( hdr, 0, sizeof(snap_acc_hdr_t) );
     647           0 :   hdr->pubkey    = ctx->disk.pubkey;
     648           0 :   hdr->owner     = ctx->disk.owner;
     649           0 :   hdr->lamports   = FD_VOLATILE_CONST( accmeta->lamports );
     650           0 :   hdr->executable = !!FD_ACCDB_SIZE_EXEC( FD_VOLATILE_CONST( accmeta->executable_size ) );
     651           0 :   hdr->data_len   = data_len;
     652             : 
     653           0 :   ctx->raw_buf.size += sizeof(snap_acc_hdr_t);
     654           0 :   ctx->disk.data_rem = data_len;
     655           0 :   ctx->disk.data_pad = fd_ulong_align_up( data_len, 8UL ) - data_len;
     656           0 :   return (ulong)frag->data_sz;
     657           0 : }
     658             : 
     659             : static void
     660             : msg_acc_disk( fd_snapzp_t * ctx,
     661             :               ulong         seq,       /* unused */
     662             :               ulong         sig,       /* data pointer */
     663             :               ulong         chunk,     /* descriptor pointer */
     664             :               ulong         sz,        /* descriptor size */
     665             :               ulong         ctl,       /* holds orig, som, eom */
     666             :               ulong         tsorig,    /* unused */
     667           0 :               ulong         tspub ) {  /* data size */
     668           0 :   (void)seq; (void)tsorig;
     669           0 :   int som = fd_frag_meta_ctl_som( ctl );
     670           0 :   int eom = fd_frag_meta_ctl_eom( ctl );
     671             : 
     672             :   /* begin defrag operation */
     673           0 :   ulong frag_sz = tspub;
     674           0 :   if( FD_UNLIKELY( som ) ) {
     675           0 :     FD_CHECK_CRIT( chunk>=ctx->snapmk_zp_chunk0 && chunk<=ctx->snapmk_zp_wmark && sz==sizeof(fd_backup_disk_msg_t),
     676           0 :                    "input frag bounds check failed: fd_backup_disk_msg_t(som=1)" );
     677           0 :     fd_backup_disk_msg_t const * frag = fd_chunk_to_laddr_const( ctx->snapmk_zp_mem, chunk );
     678           0 :     frag_sz = msg_acc_disk_start( ctx, frag );
     679           0 :     if( FD_UNLIKELY( tspub!=frag_sz ) ) {
     680           0 :       FD_LOG_CRIT(( "invalid accdb disk frag stream: size mismatch (%lu != %lu)", tspub, frag_sz ));
     681           0 :     }
     682           0 :   } else {
     683           0 :     FD_CHECK_CRIT( !sz, "invalid accdb disk frag stream: non-SOM frag must not have a descriptor" );
     684           0 :   }
     685           0 :   FD_CHECK_CRIT( !!ctx->disk.active, "invalid accdb disk frag stream: non-SOM frag seen but no active defrag op" );
     686             : 
     687             :   /* locate data pointer */
     688           0 :   uchar const * frag = NULL;
     689           0 :   if( FD_LIKELY( ctx->disk.data_rem ) ) {
     690           0 :     frag = fd_wksp_laddr_fast( ctx->snaprd_mem, sig );
     691           0 :   }
     692             : 
     693             :   /* defrag copy */
     694           0 :   ulong take = fd_ulong_min( ctx->disk.data_rem, frag_sz );
     695           0 :   if( FD_LIKELY( take ) ) {
     696           0 :     FD_CHECK_CRIT( ctx->raw_buf.size + take <= RAW_BUF_SZ,
     697           0 :                    "internal bounds check failed" );
     698           0 :     FD_CHECK_CRIT( (ulong)frag           >= ctx->snaprd_data0 &&
     699           0 :                    (ulong)frag + frag_sz <= ctx->snaprd_data1,
     700           0 :                    "snaprd bounds check failed" );
     701           0 :     fd_memcpy( ctx->raw + ctx->raw_buf.size, frag, take );
     702           0 :     ctx->raw_buf.size  += take;
     703           0 :     ctx->disk.data_rem -= take;
     704           0 :     frag_sz            -= take;
     705           0 :   }
     706           0 :   FD_CHECK_CRIT( !frag_sz, "invalid accdb disk frag stream: frag spans multiple accounts" );
     707           0 :   FD_CHECK_CRIT( !( !ctx->disk.data_rem && !eom ), "invalid accdb disk frag stream: non-EOM frag seen but data already complete" );
     708             : 
     709             :   /* finish defrag operation */
     710           0 :   if( eom ) {
     711           0 :     FD_CHECK_CRIT( !ctx->disk.data_rem, "invalid accdb disk frag stream: EOM frag seen but defrag not complete" );
     712           0 :     if( ctx->disk.data_pad ) {
     713           0 :       FD_TEST( ctx->raw_buf.size + ctx->disk.data_pad <= RAW_BUF_SZ );
     714           0 :       fd_memset( ctx->raw + ctx->raw_buf.size, 0, ctx->disk.data_pad );
     715           0 :       ctx->raw_buf.size += ctx->disk.data_pad;
     716           0 :     }
     717           0 :     ctx->snapshot_account_cnt++;
     718           0 :     ctx->snapshot_account_sz += sizeof(snap_acc_hdr_t) + fd_ulong_align_up( (ulong)FD_ACCDB_SIZE_DATA( ctx->disk.size ), 8UL );
     719           0 :     ctx->snapshot_disk_account_cnt++;
     720           0 :     ctx->metrics.accounts_compressed++;
     721           0 :     memset( &ctx->disk, 0, sizeof(ctx->disk) );
     722           0 :   }
     723           0 : }
     724             : 
     725             : /* msg_acc_disk instructions snapzp to compress a batch of unfragmented
     726             :    accounts that were read from the accdb disk file.  (fast path)  */
     727             : 
     728             : static void
     729             : msg_acc_disk_batch( fd_snapzp_t *                      ctx,
     730             :                     fd_backup_disk_batch_msg_t const * batch,
     731           0 :                     ulong                              sig ) {
     732           0 :   FD_CHECK_CRIT( ctx->snap_fd>=0, "invalid snapshot file descriptor" );
     733           0 :   FD_CHECK_CRIT( !ctx->disk.active, "received account batch while already processing a disk account" );
     734             : 
     735           0 :   fd_backup_accidx_t const * idx      = &ctx->acc_cache->idx;
     736           0 :   fd_accdb_accmeta_t const * acc_pool = idx->acc_pool;
     737             : 
     738             :   /* MLP gather of account index entries */
     739           0 :   static fd_accdb_accmeta_t const dead = {0};
     740           0 :   fd_accdb_accmeta_t const * gather[ FD_BACKUP_DISK_PARA ];
     741           0 :   for( ulong i=0UL; i<FD_BACKUP_DISK_PARA; i++ ) {
     742           0 :     uint ai = batch->acc_idx[ i ];
     743           0 :     gather[ i ] = fd_backup_accidx_valid( idx, ai ) ? &acc_pool[ ai ] : &dead;
     744           0 :   }
     745           0 :   ulong lamports[ FD_BACKUP_DISK_PARA ];
     746           0 :   uint  exec_sz [ FD_BACKUP_DISK_PARA ];
     747           0 :   for( ulong i=0UL; i<FD_BACKUP_DISK_PARA; i++ ) {
     748           0 :     lamports[ i ] = FD_VOLATILE_CONST( gather[ i ]->lamports        );
     749           0 :     exec_sz [ i ] = FD_VOLATILE_CONST( gather[ i ]->executable_size );
     750           0 :   }
     751             : 
     752             :   /* Bounds check batch (TOCTOU susceptible, but this is an accepted
     753             :      risk, as snapmk->snapzp is a trusted link) */
     754           0 :   uchar const * base = fd_wksp_laddr_fast( ctx->snaprd_mem, sig );
     755           0 :   for( ulong i=0UL; i<FD_BACKUP_DISK_PARA; i++ ) {
     756           0 :     FD_CHECK_CRIT( ((ulong)base + batch->frag_off[ i ] >= ctx->snaprd_data0) &
     757           0 :                    ((ulong)base + batch->frag_off[ i ] + sizeof(fd_accdb_disk_meta_t) <= (ulong)ctx->snaprd_data1),
     758           0 :                    "account data bounds check fail" );
     759           0 :   }
     760             : 
     761             :   /* Sequential scan of disk data in frag */
     762           0 :   for( ulong i=0UL; i<FD_BACKUP_DISK_PARA; i++ ) {
     763           0 :     uint acc_idx = batch->acc_idx[ i ];
     764           0 :     if( acc_idx==UINT_MAX ) continue;
     765           0 :     FD_CHECK_CRIT( fd_backup_accidx_valid( idx, acc_idx ), "account index bounds check fail" );
     766             : 
     767           0 :     fd_accdb_disk_meta_t const * dm = (fd_accdb_disk_meta_t const *)( base + batch->frag_off[ i ] );
     768           0 :     FD_CHECK_CRIT( (ulong)(dm+1) <= (ulong)ctx->snaprd_data1, "account data bounds check fail" );
     769             : 
     770           0 :     ulong data_len = (ulong)FD_ACCDB_SIZE_DATA( dm->size );
     771           0 :     FD_CHECK_CRIT( (ulong)(dm+1)+data_len <= (ulong)ctx->snaprd_data1, "account data bounds check fail" );
     772             : 
     773             :     /* validate that disk data matches index */
     774           0 :     FD_CHECK_CRIT( FD_ACCDB_SIZE_DATA( exec_sz[ i ] )==data_len, "account query corruption detected" );
     775           0 :     FD_CHECK_CRIT( !memcmp( gather[ i ]->key.pubkey, dm->pubkey, sizeof(fd_pubkey_t) ), "account query corruption detected" );
     776             : 
     777           0 :     ulong rec_sz   = sizeof(snap_acc_hdr_t) + fd_ulong_align_up( data_len, 8UL );
     778           0 :     ulong data_pad = fd_ulong_align_up( data_len, 8UL ) - data_len;
     779           0 :     FD_CHECK_CRIT( rec_sz<=RAW_BUF_SZ, "oversize snapshot account record" );
     780           0 :     if( FD_UNLIKELY( ctx->raw_buf.size + rec_sz > RAW_BUF_SZ ) ) {
     781           0 :       zip_flush( ctx );
     782           0 :     }
     783             : 
     784           0 :     snap_acc_hdr_t * hdr = (snap_acc_hdr_t *)( ctx->raw + ctx->raw_buf.size );
     785           0 :     memset( hdr, 0, sizeof(snap_acc_hdr_t) );
     786           0 :     memcpy( hdr->pubkey.uc, dm->pubkey, sizeof(fd_pubkey_t) );
     787           0 :     memcpy( hdr->owner.uc,  dm->owner,  sizeof(fd_pubkey_t) );
     788           0 :     hdr->lamports   = lamports[ i ];
     789           0 :     hdr->executable = !!FD_ACCDB_SIZE_EXEC( exec_sz[ i ] );
     790           0 :     hdr->data_len   = data_len;
     791           0 :     ctx->raw_buf.size += sizeof(snap_acc_hdr_t);
     792             : 
     793           0 :     if( FD_LIKELY( data_len ) ) {
     794           0 :       uchar const * data = base + batch->frag_off[ i ] + sizeof(fd_accdb_disk_meta_t);
     795           0 :       fd_memcpy( ctx->raw + ctx->raw_buf.size, data, data_len );
     796           0 :       ctx->raw_buf.size += data_len;
     797           0 :     }
     798           0 :     if( data_pad ) {
     799           0 :       fd_memset( ctx->raw + ctx->raw_buf.size, 0, data_pad );
     800           0 :       ctx->raw_buf.size += data_pad;
     801           0 :     }
     802           0 :     ctx->snapshot_account_cnt++;
     803           0 :     ctx->snapshot_account_sz += rec_sz;
     804           0 :     ctx->snapshot_disk_account_cnt++;
     805           0 :     ctx->metrics.accounts_compressed++;
     806           0 :   }
     807           0 : }
     808             : 
     809             : /* msg_done is called on all snapzp tiles once all account data has been
     810             :    compressed.  This causes snapzp to enter sleep state. */
     811             : 
     812             : static void
     813           0 : msg_done( fd_snapzp_t * ctx ) {
     814             :   /* Leave file descriptors open, but release local reference */
     815           0 :   ctx->snap_fd = -1;
     816           0 : }
     817             : 
     818             : /* returnable_frag is called for every message received from snapmk. */
     819             : 
     820             : static int
     821             : returnable_frag( fd_snapzp_t *       ctx,
     822             :                  ulong               in_idx,
     823             :                  ulong               seq,
     824             :                  ulong               sig,
     825             :                  ulong               chunk,
     826             :                  ulong               sz,
     827             :                  ulong               ctl,
     828             :                  ulong               tsorig,
     829             :                  ulong               tspub,
     830           0 :                  fd_stem_context_t * stem ) {
     831           0 :   (void)in_idx; (void)stem;
     832           0 :   ctx->idle_cnt = 0UL;
     833           0 :   ulong orig = fd_frag_meta_ctl_orig( ctl );
     834             : 
     835           0 : # define MSG_TRANSLATE( msg_type ) __extension__({ \
     836           0 :     FD_CHECK_CRIT( chunk >= ctx->snapmk_zp_chunk0 && chunk <= ctx->snapmk_zp_wmark && sz==sizeof(msg_type), "input frag bounds check failed: " #msg_type ); \
     837           0 :     (msg_type const *)fd_chunk_to_laddr_const( ctx->snapmk_zp_mem, chunk ); \
     838           0 :   })
     839             : 
     840           0 :   switch( orig ) {
     841           0 :   case FD_BACKUP_ORIG_START:
     842           0 :     msg_start( ctx, MSG_TRANSLATE( fd_backup_start_msg_t ) );
     843           0 :     break;
     844           0 :   case FD_BACKUP_ORIG_ACC_CACHE:
     845           0 :     msg_acc_cache( ctx, MSG_TRANSLATE( fd_backup_cache_msg_t ) );
     846           0 :     zip_work( ctx );
     847           0 :     break;
     848           0 :   case FD_BACKUP_ORIG_ACC_DELTA:
     849           0 :     msg_acc_delta( ctx, MSG_TRANSLATE( fd_backup_delta_msg_t ) );
     850           0 :     zip_work( ctx );
     851           0 :     break;
     852           0 :   case FD_BACKUP_ORIG_ACC_DISK:
     853           0 :     msg_acc_disk( ctx, seq, sig, chunk, sz, ctl, tsorig, tspub );
     854           0 :     zip_work( ctx );
     855           0 :     break;
     856           0 :   case FD_BACKUP_ORIG_ACC_DISK_BATCH:
     857           0 :     msg_acc_disk_batch( ctx, MSG_TRANSLATE( fd_backup_disk_batch_msg_t ), sig );
     858           0 :     zip_work( ctx );
     859           0 :     break;
     860           0 :   case FD_BACKUP_ORIG_FLUSH:
     861           0 :     zip_flush( ctx );
     862           0 :     fd_backup_worker_stats_t * stats = &ctx->stats->worker[ ctx->kind_id ];
     863           0 :     __atomic_store_n( &stats->account_cnt,         ctx->snapshot_account_cnt,        __ATOMIC_RELAXED );
     864           0 :     __atomic_store_n( &stats->account_sz,          ctx->snapshot_account_sz,         __ATOMIC_RELAXED );
     865           0 :     __atomic_store_n( &stats->tombstone_cnt,       ctx->snapshot_tombstone_cnt,      __ATOMIC_RELAXED );
     866           0 :     __atomic_store_n( &stats->cached_account_cnt,  ctx->snapshot_cached_account_cnt, __ATOMIC_RELAXED );
     867           0 :     __atomic_store_n( &stats->disk_account_cnt,    ctx->snapshot_disk_account_cnt,   __ATOMIC_RELAXED );
     868           0 :     __atomic_store_n( &stats->zstd_data_frame_cnt, 2UL*ctx->frame_cnt,                __ATOMIC_RELAXED );
     869           0 :     __atomic_store_n( &stats->zstd_padding_sz,     ctx->snapshot_zstd_padding_sz,    __ATOMIC_RELAXED );
     870           0 :     __atomic_store_n( &stats->compress_ticks,      ctx->metrics.compress_ticks-ctx->snapshot_compress_ticks0,     __ATOMIC_RELAXED );
     871           0 :     __atomic_store_n( &stats->io_blocked_ticks,    ctx->metrics.io_blocked_ticks-ctx->snapshot_io_blocked_ticks0, __ATOMIC_RELAXED );
     872           0 :     __atomic_store_n( &stats->uncompressed_sz,     ctx->snapshot_uncompressed_sz,    __ATOMIC_RELEASE );
     873           0 :     break;
     874           0 :   case FD_BACKUP_ORIG_DONE:
     875           0 :     msg_done( ctx );
     876           0 :     break;
     877           0 :   default:
     878           0 :     FD_LOG_CRIT(( "unknown backup instruction (orig=%lu, seq=%lu)", orig, seq ));
     879           0 :   }
     880             : 
     881           0 :   return 0;
     882           0 : }
     883             : 
     884             : static void
     885           0 : metrics_write( fd_snapzp_t * ctx ) {
     886           0 :   FD_MCNT_SET( SNAPZP, ACCOUNT_COMPRESSED,          ctx->metrics.accounts_compressed );
     887           0 :   FD_MCNT_SET( SNAPZP, COMPRESS_CONSUMED_BYTES,     ctx->metrics.bytes_compressed    );
     888           0 :   FD_MCNT_SET( SNAPZP, DISK_WRITTEN_BYTES,          ctx->metrics.bytes_written       );
     889           0 :   FD_MCNT_SET( SNAPZP, IO_BLOCKED_DURATION_NANOS,   ctx->metrics.io_blocked_ticks    );
     890           0 :   FD_MCNT_SET( SNAPZP, COMPRESS_DURATION_NANOS,     ctx->metrics.compress_ticks      );
     891           0 :   FD_MCNT_SET( SNAPZP, CACHE_READ_TORN,             ctx->metrics.cache_read_torn     );
     892           0 : }
     893             : 
     894           0 : #define STEM_BURST 1UL
     895           0 : #define STEM_LAZY  9400UL
     896           0 : #define STEM_CALLBACK_CONTEXT_TYPE    fd_snapzp_t
     897           0 : #define STEM_CALLBACK_CONTEXT_ALIGN   alignof(fd_snapzp_t)
     898           0 : #define STEM_CALLBACK_BEFORE_CREDIT   before_credit
     899           0 : #define STEM_CALLBACK_RETURNABLE_FRAG returnable_frag
     900           0 : #define STEM_CALLBACK_METRICS_WRITE   metrics_write
     901             : #include "../../disco/stem/fd_stem.c"
     902             : 
     903             : #ifndef FD_TILE_TEST
     904             : fd_topo_run_tile_t fd_tile_snapzp = {
     905             :   .name                     = "snapzp",
     906             :   .populate_allowed_fds     = populate_allowed_fds,
     907             :   .populate_allowed_seccomp = populate_allowed_seccomp,
     908             :   .scratch_align            = scratch_align,
     909             :   .scratch_footprint        = scratch_footprint,
     910             :   .privileged_init          = privileged_init,
     911             :   .unprivileged_init        = unprivileged_init,
     912             :   .run                      = stem_run,
     913             : };
     914             : #endif

Generated by: LCOV version 1.14