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: 2025-01-08 12:08:44 Functions: 6 6 100.0 %

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

Generated by: LCOV version 1.14