LCOV - code coverage report
Current view: top level - util/wksp - fd_wksp_checkpt_v1.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 100 119 84.0 %
Date: 2026-09-17 04:28:31 Functions: 6 6 100.0 %

          Line data    Source code
       1             : #include "fd_wksp_private.h"
       2             : #include "../tpool/fd_tpool.h"
       3             : 
       4             : #include <errno.h>
       5             : #include <unistd.h>
       6             : #include <fcntl.h>
       7             : #include <sys/stat.h>
       8             : 
       9             : /* fd_wksp_private_checkpt_v1_write writes size sz buffer buf to the
      10             :    output stream checkpt.  Assumes checkpt is valid and not in a
      11             :    prepare.  Returns 0 on success and non-zero on failure (will be an
      12             :    errno compat error code). */
      13             : 
      14             : static inline int
      15             : fd_wksp_private_checkpt_v1_write( fd_io_buffered_ostream_t * checkpt,
      16             :                                   void const *               buf,
      17           9 :                                   ulong                      sz ) {
      18           9 :   return fd_io_buffered_ostream_write( checkpt, buf, sz );
      19           9 : }
      20             : 
      21             : /* fd_wksp_private_checkpt_v1_prepare prepares to write at most max
      22             :    bytes to the output stream checkpt.  Assumes checkpt is valid and not
      23             :    in a prepare and max is at most checkpt's wbuf_sz.  Returns the
      24             :    location in the caller's address space for preparing the max bytes on
      25             :    success (*_err will be 0) and NULL on failure (*_err will be an errno
      26             :    compat error code). */
      27             : 
      28             : static inline void *
      29             : fd_wksp_private_checkpt_v1_prepare( fd_io_buffered_ostream_t * checkpt,
      30             :                                     ulong                      max,
      31          27 :                                     int *                      _err ) {
      32          27 :   if( FD_UNLIKELY( fd_io_buffered_ostream_peek_sz( checkpt )<max ) ) {
      33           0 :     int err = fd_io_buffered_ostream_flush( checkpt );
      34           0 :     if( FD_UNLIKELY( err ) ) {
      35           0 :       *_err = err;
      36           0 :       return NULL;
      37           0 :     }
      38             :     /* At this point, peek_sz==wbuf_sz and wbuf_sz>=max */
      39           0 :   }
      40             :   /* At this point, peek_sz>=max */
      41          27 :   *_err = 0;
      42          27 :   return fd_io_buffered_ostream_peek( checkpt );
      43          27 : }
      44             : 
      45             : /* fd_wksp_private_checkpt_v1_publish publishes prepared bytes
      46             :    [prepare,next) to checkpt.  Assumes checkpt is in a prepare and the
      47             :    number of bytes to publish is at most the prepare's max.  checkpt
      48             :    will not be in a prepare on return. */
      49             : 
      50             : static inline void
      51             : fd_wksp_private_checkpt_v1_publish( fd_io_buffered_ostream_t * checkpt,
      52          27 :                                     void *                     next ) {
      53          27 :   fd_io_buffered_ostream_seek( checkpt, (ulong)next - (ulong)fd_io_buffered_ostream_peek( checkpt ) );
      54          27 : }
      55             : 
      56             : /* fd_wksp_private_checkpt_v1_cancel cancels a prepare.  Assumes checkpt
      57             :    is valid and in a prepare.  checkpt will not be in a prepare on
      58             :    return. */
      59             : 
      60             : //static inline void fd_wksp_private_checkpt_v1_cancel( fd_io_buffered_ostream_t * checkpt ) { (void)checkpt; }
      61             : 
      62             : /* fd_wksp_private_checkpt_v1_ulong checkpoints the value v into a
      63             :    checkpt.  p points to the location in a prepare where v should be
      64             :    encoded.  Assumes this location has svw_enc_sz(v) available (at least
      65             :    1 and at most 9).  Returns the location of the first byte after the
      66             :    encoded value (will be prep+svw_enc_sz(val)). */
      67             : 
      68         234 : static inline void * fd_wksp_private_checkpt_v1_ulong( void * prep, ulong val ) { return fd_ulong_svw_enc( (uchar *)prep, val ); }
      69             : 
      70             : /* fd_wksp_private_checkpt_v1_buf checkpoints a variable length buffer buf
      71             :    of size sz into a checkpt.  p points to the location in a prepare
      72             :    region where buf should be encoded.  Assumes this location has
      73             :    svw_enc_sz(sz)+sz bytes available (at least 1+sz and at most 9+sz).
      74             :    Returns the location of the first byte after the encoded buffer (will
      75             :    be prep+svw_enc_sz(sz)+sz).  Zero sz is fine (and NULL buf is fine if
      76             :    sz is zero). */
      77             : 
      78             : static inline void *
      79             : fd_wksp_private_checkpt_v1_buf( void *       prep,
      80             :                                 void const * buf,
      81          81 :                                 ulong        sz ) {
      82          81 :   prep = fd_wksp_private_checkpt_v1_ulong( (uchar *)prep, sz );
      83          81 :   if( FD_LIKELY( sz ) ) fd_memcpy( prep, buf, sz );
      84          81 :   return (uchar *)prep + sz;
      85          81 : }
      86             : 
      87             : int
      88             : fd_wksp_private_checkpt_v1( fd_tpool_t * tpool,
      89             :                             ulong        t0,
      90             :                             ulong        t1,
      91             :                             fd_wksp_t *  wksp,
      92             :                             char const * path,
      93             :                             ulong        mode,
      94          12 :                             char const * uinfo ) {
      95          12 :   (void)tpool; (void)t0; (void)t1; /* Note: Thread parallel v1 checkpoint not supported */
      96             : 
      97          12 :   char const * binfo = fd_log_build_info;
      98             : 
      99             : //FD_LOG_INFO(( "Checkpt wksp \"%s\" to \"%s\" (mode 0%03lo), uinfo \"%s\"", wksp->name, path, mode, uinfo ));
     100             : 
     101          12 :   mode_t old_mask = umask( (mode_t)0 );
     102          12 :   int fd = open( path, O_CREAT|O_EXCL|O_WRONLY, (mode_t)mode );
     103          12 :   umask( old_mask );
     104          12 :   if( FD_UNLIKELY( fd==-1 ) ) {
     105           3 :     FD_LOG_WARNING(( "open(\"%s\",O_CREAT|O_EXCL|O_WRONLY,0%03lo) failed (%i-%s)", path, mode, errno, fd_io_strerror( errno ) ));
     106           3 :     return FD_WKSP_ERR_FAIL;
     107           3 :   }
     108             : 
     109           9 : # define WBUF_ALIGN     ( 4096UL)
     110          18 : # define WBUF_FOOTPRINT (65536UL)
     111             : 
     112           9 :   uchar                    wbuf[ WBUF_FOOTPRINT ] __attribute__((aligned(WBUF_ALIGN)));
     113           9 :   fd_io_buffered_ostream_t checkpt[ 1 ];
     114           9 :   fd_io_buffered_ostream_init( checkpt, fd, wbuf, WBUF_FOOTPRINT );
     115             : 
     116           9 :   int     err;
     117           9 :   uchar * prep;
     118             : 
     119           9 :   err = fd_wksp_private_lock( wksp ); if( FD_UNLIKELY( err ) ) goto fini; /* logs details */
     120             : 
     121             :   /* Do basic wksp checks */
     122             : 
     123           9 :   ulong data_lo = wksp->gaddr_lo;
     124           9 :   ulong data_hi = wksp->gaddr_hi;
     125           9 :   if( FD_UNLIKELY( !((0UL<data_lo) & (data_lo<=data_hi)) ) ) goto corrupt_wksp;
     126             : 
     127             :   //FD_LOG_INFO(( "Checkpt header and metadata" ));
     128             : 
     129           9 :   ulong binfo_len = fd_cstr_nlen( binfo, FD_WKSP_CHECKPT_V1_BINFO_MAX-1UL );
     130           9 :   ulong uinfo_len = fd_cstr_nlen( uinfo, FD_WKSP_CHECKPT_V1_UINFO_MAX-1UL );
     131             : 
     132           9 :   prep = fd_wksp_private_checkpt_v1_prepare( checkpt, WBUF_FOOTPRINT, &err ); if( FD_UNLIKELY( !prep ) ) goto io_err;
     133           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, wksp->magic                                );
     134           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, (ulong)FD_WKSP_CHECKPT_STYLE_V1            );
     135           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, (ulong)wksp->seed                          );
     136           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, wksp->part_max                             );
     137           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, wksp->data_max                             );
     138           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, (ulong)fd_log_wallclock()                  );
     139           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_app_id()                            );
     140           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_thread_id()                         );
     141           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_host_id()                           );
     142           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_cpu_id()                            );
     143           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_group_id()                          );
     144           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_tid()                               );
     145           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_user_id()                           );
     146           9 :   prep = fd_wksp_private_checkpt_v1_buf  ( prep, wksp->name,      strlen( wksp->name      ) );
     147           9 :   prep = fd_wksp_private_checkpt_v1_buf  ( prep, fd_log_app(),    strlen( fd_log_app()    ) );
     148           9 :   prep = fd_wksp_private_checkpt_v1_buf  ( prep, fd_log_thread(), strlen( fd_log_thread() ) );
     149           9 :   prep = fd_wksp_private_checkpt_v1_buf  ( prep, fd_log_host(),   strlen( fd_log_host()   ) );
     150           9 :   prep = fd_wksp_private_checkpt_v1_buf  ( prep, fd_log_cpu(),    strlen( fd_log_cpu()    ) );
     151           9 :   prep = fd_wksp_private_checkpt_v1_buf  ( prep, fd_log_group(),  strlen( fd_log_group()  ) );
     152           9 :   prep = fd_wksp_private_checkpt_v1_buf  ( prep, fd_log_user(),   strlen( fd_log_user()   ) );
     153           9 :   prep = fd_wksp_private_checkpt_v1_buf  ( prep, binfo,           binfo_len                 );
     154           9 :   prep = fd_wksp_private_checkpt_v1_buf  ( prep, uinfo,           uinfo_len                 );
     155           9 :   fd_wksp_private_checkpt_v1_publish( checkpt, prep );
     156             : 
     157             : //FD_LOG_INFO(( "Checkpt allocations" ));
     158             : 
     159           9 :   ulong part_max = wksp->part_max;
     160           9 :   fd_wksp_private_pinfo_t * pinfo = fd_wksp_private_pinfo( wksp );
     161             : 
     162           9 :   ulong cycle_tag = wksp->cycle_tag++;
     163             : 
     164           9 :   ulong gaddr_last = data_lo;
     165             : 
     166           9 :   ulong i = fd_wksp_private_pinfo_idx( wksp->part_head_cidx );
     167          30 :   while( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
     168          21 :     if( FD_UNLIKELY( i>=part_max ) || FD_UNLIKELY( pinfo[ i ].cycle_tag==cycle_tag ) ) goto corrupt_wksp;
     169          21 :     pinfo[ i ].cycle_tag = cycle_tag; /* mark i as visited */
     170             : 
     171             :     /* Do basic partition checks */
     172             : 
     173          21 :     ulong gaddr_lo = pinfo[ i ].gaddr_lo;
     174          21 :     ulong gaddr_hi = pinfo[ i ].gaddr_hi;
     175          21 :     ulong tag      = pinfo[ i ].tag;
     176             : 
     177          21 :     if( FD_UNLIKELY( !((gaddr_last==gaddr_lo) & (gaddr_lo<gaddr_hi) & (gaddr_hi<=data_hi)) ) ) goto corrupt_wksp;
     178             : 
     179          21 :     gaddr_last = gaddr_hi;
     180             : 
     181             :     /* If an allocated partition, checkpt it */
     182             : 
     183          21 :     if( tag ) { /* ~50/50 */
     184             : 
     185           9 :       ulong sz = gaddr_hi - gaddr_lo;
     186           9 :       void * laddr_lo = fd_wksp_laddr_fast( wksp, gaddr_lo );
     187             : 
     188             :       /* Checkpt partition header */
     189             : 
     190           9 :       prep = fd_wksp_private_checkpt_v1_prepare( checkpt, 3UL*9UL, &err ); if( FD_UNLIKELY( !prep ) ) goto io_err;
     191           9 :       prep = fd_wksp_private_checkpt_v1_ulong( prep, tag      );
     192           9 :       prep = fd_wksp_private_checkpt_v1_ulong( prep, gaddr_lo );
     193           9 :       prep = fd_wksp_private_checkpt_v1_ulong( prep, sz       );
     194           9 :       fd_wksp_private_checkpt_v1_publish( checkpt, prep );
     195             : 
     196             :       /* Checkpt partition data */
     197             : #     if FD_HAS_DEEPASAN
     198             :       /* We read the whole partition, so unpoison the holes sub-allocators
     199             :          leave poisoned (permanently discarding that finer poisoning). */
     200             :       fd_asan_unpoison( laddr_lo, sz );
     201             : #     endif
     202             : 
     203           9 :       err = fd_wksp_private_checkpt_v1_write( checkpt, laddr_lo, sz ); if( FD_UNLIKELY( err ) ) goto io_err;
     204           9 :     }
     205             : 
     206             :     /* Advance to next partition */
     207             : 
     208          21 :     i = fd_wksp_private_pinfo_idx( pinfo[ i ].next_cidx );
     209          21 :   }
     210             : 
     211             : //FD_LOG_INFO(( "Checkpt footer" ));
     212             : 
     213           9 :   prep = fd_wksp_private_checkpt_v1_prepare( checkpt, 1UL*9UL, &err ); if( FD_UNLIKELY( !prep ) ) goto io_err;
     214           9 :   prep = fd_wksp_private_checkpt_v1_ulong( prep, 0UL ); /* tags are never 0 above */
     215           9 :   fd_wksp_private_checkpt_v1_publish( checkpt, prep );
     216             : 
     217           9 :   err = fd_io_buffered_ostream_flush( checkpt ); if( FD_UNLIKELY( err ) ) goto io_err;
     218             : 
     219           9 :   fd_wksp_private_unlock( wksp );
     220             : 
     221             : //FD_LOG_INFO(( "Checkpt successful" ));
     222             : 
     223             :   /* note: err == 0 at this point */
     224             : 
     225           9 : fini: /* note: wksp unlocked at this point */
     226           9 :   fd_io_buffered_ostream_fini( checkpt );
     227           9 :   if( FD_UNLIKELY( err ) && FD_UNLIKELY( unlink( path ) ) )
     228           0 :     FD_LOG_WARNING(( "unlink(\"%s\") failed (%i-%s); attempting to continue", path, errno, fd_io_strerror( errno ) ));
     229           9 :   if( FD_UNLIKELY( close( fd ) ) )
     230           0 :     FD_LOG_WARNING(( "close(\"%s\") failed (%i-%s); attempting to continue", path, errno, fd_io_strerror( errno ) ));
     231           9 :   return err;
     232             : 
     233           0 : io_err: /* Failed due to I/O error ... clean up and log (note: wksp locked at this point) */
     234           0 :   fd_wksp_private_unlock( wksp );
     235           0 :   FD_LOG_WARNING(( "Checkpt wksp \"%s\" to \"%s\" failed due to I/O error (%i-%s)",
     236           0 :                    wksp->name, path, err, fd_io_strerror( err ) ));
     237           0 :   err = FD_WKSP_ERR_FAIL;
     238           0 :   goto fini;
     239             : 
     240           0 : corrupt_wksp: /* Failed due to wksp corruption ... clean up and log (note: wksp locked at this point) */
     241           0 :   fd_wksp_private_unlock( wksp );
     242           0 :   FD_LOG_WARNING(( "Checkpt wksp \"%s\" to \"%s\" failed due to wksp corruption", wksp->name, path ));
     243           0 :   err = FD_WKSP_ERR_CORRUPT;
     244           0 :   goto fini;
     245           9 : }

Generated by: LCOV version 1.14