LCOV - code coverage report
Current view: top level - util/wksp - fd_wksp_restore_v2.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 379 584 64.9 %
Date: 2026-08-28 06:53:58 Functions: 9 10 90.0 %

          Line data    Source code
       1             : #include "fd_wksp_private.h"
       2             : 
       3             : #include <stdio.h>
       4             : #include <errno.h>
       5             : #include <unistd.h>
       6             : #include <fcntl.h>
       7             : #include <sys/stat.h>
       8             : 
       9             : /* This is an implementation detail and not strictly part of the v2
      10             :    specification. */
      11             : 
      12             : #define FD_WKSP_RESTORE_V2_CGROUP_MAX (1024UL)
      13             : 
      14             : /* Note: restore not in frame on entry, restore at off on exit.  Jumps
      15             :    to fail on error (logs details). */
      16             : 
      17         273 : #define RESTORE_SEEK(off) do {                                                          \
      18         273 :     ulong _off = (off);                                                                 \
      19         273 :     if( FD_UNLIKELY( fd_restore_seek( restore, _off ) ) ) goto fail; /* logs details */ \
      20         273 :   } while(0)
      21             : 
      22             : /* Note: restore not in frame and at start of frame on entry, restore in
      23             :    frame on exit.  Jumps to fail on error (logs details). */
      24             : 
      25         462 : #define RESTORE_OPEN(frame_style) do {                                                                                \
      26         462 :     if( FD_UNLIKELY( fd_restore_open_advanced( restore, (frame_style), &frame_off ) ) ) goto fail; /* logs details */ \
      27         462 :   } while(0)
      28             : 
      29             : /* Note: restore in frame on entry, restore just after frame on exit.
      30             :    Assumes frame fully processed.  Jumps to fail on error (logs
      31             :    details).  */
      32             : 
      33         462 : #define RESTORE_CLOSE() do {                                                                            \
      34         462 :     if( FD_UNLIKELY( fd_restore_close_advanced( restore, &frame_off ) ) ) goto fail; /* logs details */ \
      35         462 :   } while(0)
      36             : 
      37             : /* Note: restore in frame at meta and sz must be at most
      38             :    FD_RESTORE_META_MAX on entry, restore in frame at just past meta with
      39             :    meta ready on exit.  Jumps to fail on error (logs details). */
      40             : 
      41         348 : #define RESTORE_META( meta, sz ) do {                                        \
      42         348 :     ulong _sz  = (sz);                                                       \
      43         348 :     int   _err = fd_restore_meta( restore, (meta), _sz ); /* logs details */ \
      44         348 :     if( FD_UNLIKELY( _err ) ) {                                              \
      45           0 :       FD_LOG_WARNING(( "fd_restore_meta( %s, %lu ) failed (%i-%s)",          \
      46           0 :                        #meta, _sz, _err, fd_checkpt_strerror( _err ) ));     \
      47           0 :       goto fail;                                                             \
      48           0 :     }                                                                        \
      49         348 :   } while(0)
      50             : 
      51             : /* Note: restore in frame at data on entry, restore in frame just past
      52             :    data on exit, data potentially not ready until frame close and should
      53             :    exist untouched until then (logs details). */
      54             : 
      55         516 : #define RESTORE_DATA( data, sz ) do {                                        \
      56         516 :     ulong _sz  = (sz);                                                       \
      57         516 :     int   _err = fd_restore_data( restore, (data), _sz ); /* logs details */ \
      58         516 :     if( FD_UNLIKELY( _err ) ) {                                              \
      59           0 :       FD_LOG_WARNING(( "fd_restore_data( %s, %lu ) failed (%i-%s)",          \
      60           0 :                        #data, _sz, _err, fd_checkpt_strerror( _err ) ));     \
      61           0 :       goto fail;                                                             \
      62           0 :     }                                                                        \
      63         516 :   } while(0)
      64             : 
      65             : /* Note: jumps to fail if c is not true (logs details) */
      66             : 
      67        5022 : #define RESTORE_TEST( c ) do {                          \
      68        5022 :     if( FD_UNLIKELY( !(c) ) ) {                         \
      69           0 :       FD_LOG_WARNING(( "restore test %s failed", #c )); \
      70           0 :       goto fail;                                        \
      71           0 :     }                                                   \
      72        5022 :   } while(0)
      73             : 
      74             : /* fd_wksp_restore_v2_hdr restores the header frame from a wksp checkpt.
      75             :    Assumes restore is valid and at the frame start and hdr is valid.  On
      76             :    success, returns SUCCESS, *hdr will be populated with a valid data
      77             :    and restore will be just after the frame end.  On failure, returns
      78             :    FAIL, *hdr is clobbered and the caller should not assume anything
      79             :    about the restore state. */
      80             : 
      81             : static int
      82             : fd_wksp_restore_v2_hdr( fd_restore_t *             restore,
      83         126 :                         fd_wksp_checkpt_v2_hdr_t * hdr ) {
      84         126 :   ulong frame_off;
      85             : 
      86         126 :   RESTORE_OPEN( FD_CHECKPT_FRAME_STYLE_RAW );
      87         126 :   RESTORE_DATA( hdr, sizeof(fd_wksp_checkpt_v2_hdr_t) );
      88         126 :   RESTORE_CLOSE();
      89             : 
      90         126 :   ulong name_len = fd_shmem_name_len( hdr->name );
      91             :   /* FIXME: CHECK TRAILING 0 OF NAME? */
      92             : 
      93         126 :   RESTORE_TEST( hdr->magic==FD_WKSP_MAGIC                                          );
      94         126 :   RESTORE_TEST( hdr->style==FD_WKSP_CHECKPT_STYLE_V2                               );
      95         126 :   RESTORE_TEST( fd_checkpt_frame_style_is_supported( hdr->frame_style_compressed ) );
      96         126 :   RESTORE_TEST( hdr->reserved==0U                                                  );
      97         126 :   RESTORE_TEST( name_len>0UL                                                       );
      98             :   /* ignore seed (arbitrary) */
      99         126 :   RESTORE_TEST( fd_wksp_footprint( hdr->part_max, hdr->data_max )>0UL              );
     100             : 
     101         126 :   return FD_WKSP_SUCCESS;
     102             : 
     103           0 : fail:
     104           0 :   return FD_WKSP_ERR_FAIL;
     105         126 : }
     106             : 
     107             : /* fd_wksp_restore_v2_info restores the info frame from a wksp checkpt.
     108             :    Assumes restore is valid and at the frame start, hdr has info from
     109             :    the corresponding header, info_buf has room for buf_max bytes and
     110             :    info_cstr is valid.  On success, returns SUCCESS, *info will be
     111             :    populated with a valid data, info_cstr will be populated with
     112             :    pointers into info_buf to valid info cstr (indexed in the same order
     113             :    as the info fields) and restore will be just after the frame end.  On
     114             :    failure, returns FAIL, info, info_buf and info might be clobbered and
     115             :    the restore state is unknown. */
     116             : 
     117             : static int
     118             : fd_wksp_restore_v2_info( fd_restore_t *                   restore,
     119             :                          fd_wksp_checkpt_v2_hdr_t const * hdr,
     120             :                          fd_wksp_checkpt_v2_info_t *      info,
     121             :                          char *                           info_buf,
     122             :                          ulong                            info_buf_max,
     123         126 :                          char const *                     info_cstr[ 9 ] ) {
     124         126 :   ulong frame_off;
     125             : 
     126         126 :   RESTORE_OPEN( hdr->frame_style_compressed );
     127         126 :   RESTORE_META( info, sizeof(fd_wksp_checkpt_v2_info_t) );
     128         126 :   ulong info_buf_sz = info->sz_app
     129         126 :                     + info->sz_thread
     130         126 :                     + info->sz_host
     131         126 :                     + info->sz_cpu
     132         126 :                     + info->sz_group
     133         126 :                     + info->sz_user
     134         126 :                     + info->sz_path
     135         126 :                     + info->sz_binfo
     136         126 :                     + info->sz_uinfo;
     137         126 :   RESTORE_TEST( info_buf_sz<=info_buf_max );
     138         126 :   RESTORE_DATA( info_buf, info_buf_sz );
     139         126 :   RESTORE_CLOSE();
     140             : 
     141         126 :   char const * p = info_buf;
     142             : 
     143        1134 : # define NEXT( sz, max ) (__extension__({                   \
     144        1134 :     char const * _cstr = p;                                 \
     145        1134 :     ulong        _sz   = (sz);                              \
     146        1134 :     ulong        _max  = (max);                             \
     147        1134 :     RESTORE_TEST( (0UL<_sz) & (_sz<=_max) );                \
     148        1134 :     RESTORE_TEST( fd_cstr_nlen( _cstr, _max )==(_sz-1UL) ); \
     149        1134 :     p += _sz;                                               \
     150        1134 :     _cstr;                                                  \
     151        1134 :   }))
     152             : 
     153         252 :   info_cstr[0] = NEXT( info->sz_app,    FD_LOG_NAME_MAX              );
     154         252 :   info_cstr[1] = NEXT( info->sz_thread, FD_LOG_NAME_MAX              );
     155         252 :   info_cstr[2] = NEXT( info->sz_host,   FD_LOG_NAME_MAX              );
     156         252 :   info_cstr[3] = NEXT( info->sz_cpu,    FD_LOG_NAME_MAX              );
     157         252 :   info_cstr[4] = NEXT( info->sz_group,  FD_LOG_NAME_MAX              );
     158         252 :   info_cstr[5] = NEXT( info->sz_user,   FD_LOG_NAME_MAX              );
     159         252 :   info_cstr[6] = NEXT( info->sz_path,   PATH_MAX                     );
     160         252 :   info_cstr[7] = NEXT( info->sz_binfo,  FD_WKSP_CHECKPT_V2_BINFO_MAX );
     161         252 :   info_cstr[8] = NEXT( info->sz_uinfo,  FD_WKSP_CHECKPT_V2_UINFO_MAX );
     162             : 
     163         126 : # undef NEXT
     164             : 
     165         126 :   return FD_WKSP_SUCCESS;
     166             : 
     167           0 : fail:
     168           0 :   return FD_WKSP_ERR_FAIL;
     169         126 : }
     170             : 
     171             : /* fd_wksp_restore_v2_ftr restores the footer frame from a wksp checkpt.
     172             :    Assumes restore is valid and at the frame start, hdr has info from
     173             :    the corresponding hdr and ftr is valid.  On success, returns SUCCESS,
     174             :    *ftr will be populated with a valid data and restore will be just
     175             :    after the frame end.  On failure, returns FAIL, *ftr is clobbered and
     176             :    the caller should not assume anything about the restore state.
     177             : 
     178             :    IMPORTANT SAFETY TIP!  This only validates the ftr and hdr are
     179             :    compatible.  It is up to the caller to validate alloc_cnt,
     180             :    cgroup_cnt, volume_cnt, and frame_off as those may not have been
     181             :    known when hdr was written and ftr is restored. */
     182             : 
     183             : static int
     184             : fd_wksp_restore_v2_ftr( fd_restore_t *                   restore,
     185             :                         fd_wksp_checkpt_v2_hdr_t const * hdr,
     186             :                         fd_wksp_checkpt_v2_ftr_t *       ftr,
     187          63 :                         ulong                            checkpt_sz ) {
     188          63 :   ulong frame_off;
     189             : 
     190          63 :   RESTORE_OPEN( FD_CHECKPT_FRAME_STYLE_RAW );
     191          63 :   RESTORE_DATA( ftr, sizeof(fd_wksp_checkpt_v2_ftr_t) );
     192          63 :   RESTORE_CLOSE();
     193             : 
     194          63 :   RESTORE_TEST( frame_off      ==checkpt_sz );
     195          63 :   RESTORE_TEST( ftr->checkpt_sz==checkpt_sz );
     196             : 
     197          63 :   RESTORE_TEST( ftr->data_max                        ==hdr->data_max               );
     198          63 :   RESTORE_TEST( ftr->part_max                        ==hdr->part_max               );
     199          63 :   RESTORE_TEST( ftr->seed                            ==hdr->seed                   );
     200          63 :   RESTORE_TEST( !memcmp( ftr->name, hdr->name, FD_SHMEM_NAME_MAX )                 );
     201          63 :   RESTORE_TEST( ftr->reserved                        ==hdr->reserved               );
     202          63 :   RESTORE_TEST( ftr->frame_style_compressed          ==hdr->frame_style_compressed );
     203          63 :   RESTORE_TEST( ftr->style                           ==hdr->style                  );
     204          63 :   RESTORE_TEST( ftr->unmagic                         ==~hdr->magic                 );
     205             : 
     206          63 :   return FD_WKSP_SUCCESS;
     207             : 
     208           0 : fail:
     209           0 :   return FD_WKSP_ERR_FAIL;
     210          63 : }
     211             : 
     212             : /* fd_wksp_private_restore_v2_common does the common parts of a
     213             :    streaming and a parallel wksp restore (restores the header and info
     214             :    frames and pretty prints them to the log).  Assumes wksp and restore
     215             :    are valid and restore is on the first header byte.  On success,
     216             :    returns SUCCESS and the restore will have processed the header and
     217             :    info frames and will be positioned just after the info frame.  On
     218             :    failure, returns FAIL and restore and hdr will be in an indeterminant
     219             :    state. */
     220             : 
     221             : static int
     222             : fd_wksp_private_restore_v2_common( fd_wksp_checkpt_v2_hdr_t * hdr,
     223          63 :                                    fd_restore_t *             restore ) {
     224             : 
     225          63 :   FD_LOG_INFO(( "Restoring header and info (v2 frames 0:1)" ));
     226             : 
     227          63 :   RESTORE_TEST( !fd_wksp_restore_v2_hdr( restore, hdr ) );
     228             : 
     229          63 :   fd_wksp_checkpt_v2_info_t info[1];
     230          63 :   char                      info_buf[ 65536 ];
     231          63 :   char const *              info_cstr[9];
     232             : 
     233          63 :   RESTORE_TEST( !fd_wksp_restore_v2_info( restore, hdr, info, info_buf, 65536UL, info_cstr ) );
     234             : 
     235             :   /* Note: this mirrors printf below */
     236             : 
     237          63 :   char info_wallclock[ FD_LOG_WALLCLOCK_CSTR_BUF_SZ ];
     238          63 :   fd_log_wallclock_cstr( info->wallclock, info_wallclock );
     239             : 
     240          63 :   FD_LOG_INFO(( "\n"
     241          63 :                 "\tstyle                  %-20i\n"       /* verbose 0 info */
     242          63 :                 "\tname                   %s\n"
     243          63 :                 "\tseed                   %-20u\n"
     244          63 :                 "\tpart_max               %-20lu\n"
     245          63 :                 "\tdata_max               %-20lu\n"
     246          63 :                 "\tmagic                  %016lx\n"      /* verbose 1 info */
     247          63 :                 "\twallclock              %-20li (%s)\n"
     248          63 :                 "\tapp                    %-20lu (%s)\n"
     249          63 :                 "\tthread                 %-20lu (%s)\n"
     250          63 :                 "\thost                   %-20lu (%s)\n"
     251          63 :                 "\tcpu                    %-20lu (%s)\n"
     252          63 :                 "\tgroup                  %-20lu (%s)\n"
     253          63 :                 "\ttid                    %-20lu\n"
     254          63 :                 "\tuser                   %-20lu (%s)\n"
     255          63 :                 "\tframe_style_compressed %-20i\n"       /* (v2 specific) */
     256          63 :                 "\tmode                   %03lo",        /* (v2 specific) */
     257          63 :                 hdr->style, hdr->name, hdr->seed, hdr->part_max, hdr->data_max,
     258          63 :                 hdr->magic, info->wallclock, info_wallclock,
     259          63 :                 info->app_id,    info_cstr[0],
     260          63 :                 info->thread_id, info_cstr[1],
     261          63 :                 info->host_id,   info_cstr[2],
     262          63 :                 info->cpu_id,    info_cstr[3],
     263          63 :                 info->group_id,  info_cstr[4],
     264          63 :                 info->tid,
     265          63 :                 info->user_id,   info_cstr[5],
     266          63 :                 hdr->frame_style_compressed,
     267          63 :                 info->mode ));
     268             : 
     269             :   /* The below info cstr are potentially long enough to be truncated by
     270             :      the logger.  So we break them into separate log messages to log as
     271             :      much detail as possible. */
     272             : 
     273          63 :   FD_LOG_INFO(( "path\n\t\t%s",  info_cstr[6] )); /* verbose 2 info (v2 specific) */
     274          63 :   FD_LOG_INFO(( "binfo\n\t\t%s", info_cstr[7] )); /* verbose 2 info */
     275          63 :   FD_LOG_INFO(( "uinfo\n\t\t%s", info_cstr[8] )); /* verbose 2 info */
     276             : 
     277          63 :   return FD_WKSP_SUCCESS;
     278             : 
     279           0 : fail:
     280           0 :   return FD_WKSP_ERR_FAIL;
     281          63 : }
     282             : 
     283             : /* fd_wksp_private_restore_v2_cgroup restores a cgroup's allocation into
     284             :    wksp.  hdr contains the corresponding restore header info, frame_off
     285             :    is where the cgroup frame to restore is located and partitions
     286             :    [part_lo,part_hi) are the wksp partition indices to use for this
     287             :    frame's allocations.  Assumes all inputs have already been validated.
     288             :    Returns SUCCESS (0) on success and FAIL (negative) on failure.  On
     289             :    return, in both cases, *_dirty will be 1/0 if wksp was/was not
     290             :    modified.  On error, the restore state is indeterminant. */
     291             : 
     292             : static int
     293             : fd_wksp_private_restore_v2_cgroup( fd_wksp_t *                      wksp,
     294             :                                    fd_restore_t *                   restore,
     295             :                                    fd_wksp_checkpt_v2_hdr_t const * hdr,
     296             :                                    ulong                            frame_off_lo,
     297             :                                    ulong                            frame_off_hi,
     298             :                                    ulong                            part_lo,
     299             :                                    ulong                            part_hi,
     300          21 :                                    int *                            _dirty ) {
     301          21 :   int dirty = 0;
     302             : 
     303          21 :   fd_wksp_private_pinfo_t * pinfo    = fd_wksp_private_pinfo( wksp );
     304          21 :   ulong                     data_lo  = wksp->gaddr_lo;
     305          21 :   ulong                     data_hi  = wksp->gaddr_hi;
     306             : 
     307          21 :   ulong hdr_data_lo = fd_wksp_private_data_off( hdr->part_max );
     308          21 :   ulong hdr_data_hi = hdr_data_lo + hdr->data_max;
     309             : 
     310          21 :   ulong frame_off;
     311          21 :   RESTORE_SEEK( frame_off_lo );
     312          21 :   RESTORE_OPEN( hdr->frame_style_compressed );
     313             : 
     314             :   /* For all cgroup allocation metadata */
     315             : 
     316          21 :   fd_wksp_checkpt_v2_cmd_t cmd[1];
     317             : 
     318          96 :   for( ulong part_idx=part_lo; part_idx<part_hi; part_idx++ ) {
     319             : 
     320          75 :     RESTORE_META( cmd, sizeof(fd_wksp_checkpt_v2_cmd_t) );
     321          75 :     RESTORE_TEST( fd_wksp_checkpt_v2_cmd_is_meta( cmd ) );
     322             : 
     323          75 :     ulong tag      = cmd->meta.tag;      /* non-zero */
     324          75 :     ulong gaddr_lo = cmd->meta.gaddr_lo;
     325          75 :     ulong gaddr_hi = cmd->meta.gaddr_hi;
     326             : 
     327          75 :     RESTORE_TEST( (hdr_data_lo<=gaddr_lo) & (gaddr_lo<gaddr_hi) & (gaddr_hi<=hdr_data_hi) );
     328             :     /* Note: disjoint [gaddr_lo,gaddr_hi) tested on rebuild */
     329             : 
     330          75 :     if( FD_UNLIKELY( !((data_lo<=gaddr_lo) & (gaddr_hi<=data_hi)) ) ) {
     331           0 :       FD_LOG_WARNING(( "restore failed because checkpt partition [0x%016lx,0x%016lx) tag %lu does not fit into current "
     332           0 :                        "wksp data region [0x%016lx,0x%016lx) (data_max checkpt %lu, wksp %lu)",
     333           0 :                        gaddr_lo, gaddr_hi, tag, data_lo, data_hi, hdr->data_max, wksp->data_max ));
     334           0 :       goto fail;
     335           0 :     }
     336             : 
     337          75 :     dirty = 1;
     338          75 :     pinfo[ part_idx ].gaddr_lo = gaddr_lo;
     339          75 :     pinfo[ part_idx ].gaddr_hi = gaddr_hi;
     340          75 :     pinfo[ part_idx ].tag      = tag;
     341          75 :   }
     342             : 
     343             :   /* Restore the data command */
     344             : 
     345          21 :   RESTORE_META( cmd, sizeof(fd_wksp_checkpt_v2_cmd_t) );
     346          21 :   RESTORE_TEST( fd_wksp_checkpt_v2_cmd_is_data( cmd ) );
     347             : 
     348             :   /* For all cgroup allocation data */
     349             : 
     350          96 :   for( ulong part_idx=part_lo; part_idx<part_hi; part_idx++ ) {
     351          75 :     ulong gaddr_lo = pinfo[ part_idx ].gaddr_lo;
     352          75 :     ulong gaddr_hi = pinfo[ part_idx ].gaddr_hi;
     353             : 
     354             :     /* Restore the allocation into the wksp data region */
     355             : 
     356          75 :     dirty = 1;
     357             : #   if FD_HAS_DEEPASAN
     358             :     /* Unpoison granule aligned as checkpt partitions can share a shadow
     359             :        byte with a neighbor another thread is restoring concurrently.
     360             :        The caller syncs the exact state once the threads have joined. */
     361             :     ulong asan_lo = fd_ulong_align_dn( (ulong)fd_wksp_laddr_fast( wksp, gaddr_lo ), FD_ASAN_ALIGN );
     362             :     ulong asan_hi = fd_ulong_align_up( (ulong)fd_wksp_laddr_fast( wksp, gaddr_hi ), FD_ASAN_ALIGN );
     363             :     fd_asan_unpoison( (void *)asan_lo, asan_hi - asan_lo );
     364             : #   endif
     365          75 :     RESTORE_DATA( fd_wksp_laddr_fast( wksp, gaddr_lo ), gaddr_hi - gaddr_lo );
     366          75 :   }
     367             : 
     368             :   /* Close the frame */
     369             : 
     370          21 :   RESTORE_CLOSE();
     371             : 
     372          21 :   RESTORE_TEST( (frame_off_lo<frame_off) & (frame_off<=frame_off_hi) ); /* == hi if compactly stored */
     373             : 
     374          21 :   *_dirty = dirty;
     375          21 :   return FD_WKSP_SUCCESS;
     376             : 
     377           0 : fail:
     378           0 :   *_dirty = dirty;
     379           0 :   return FD_WKSP_ERR_FAIL;
     380          21 : }
     381             : 
     382             : /* fd_wksp_private_restore_v2_node dispatches cgroup restore work to
     383             :    tpool threads [t0,t1).  If any errors were encountered while
     384             :    restoring cgroups, returns the first error encountered on the lowest
     385             :    indexed thread in the int location pointed to by _err.  If any
     386             :    modifications were made to wksp (whether or not there were errors),
     387             :    the int location pointed to by _dirty will be set to 1.  Assumes
     388             :    caller is thread t0 and threads (t0,t1) are available.  Note that we
     389             :    could do this with FD_MAP_REDUCE but FD_MAP_REDUCE assumes that
     390             :    fd_scratch space is available and we can't guarantee that here.
     391             :    Likewise, we could use tpool_exec_all with a TASKQ model but
     392             :    reduction of results is less efficient. */
     393             : 
     394             : static void
     395             : fd_wksp_private_restore_v2_node( void * tpool,
     396             :                                  ulong  tpool_t0,
     397             :                                  ulong  tpool_t1,          /* Assumes t1>t0 */
     398             :                                  void * _wksp,
     399             :                                  void * _restore,
     400             :                                  ulong  _hdr,
     401             :                                  ulong  _cgroup_frame_off,
     402             :                                  ulong  _cgroup_pinfo_lo,
     403             :                                  ulong  _cgroup_nxt,
     404             :                                  ulong  cgroup_cnt,
     405             :                                  ulong  _err,
     406          63 :                                  ulong  _dirty ) {
     407             : 
     408             :   /* This node is responsible for threads [t0,t1).  If this range has
     409             :      more than one thread, split the range into left and right halves,
     410             :      have the first right half thread handle the right half, use this
     411             :      thread to handle the left half and then reduce the results from
     412             :      the two halves. */
     413             : 
     414          63 :   ulong tpool_cnt = tpool_t1 - tpool_t0;
     415          63 :   if( tpool_cnt>1UL ) {
     416           0 :     ulong tpool_ts = tpool_t0 + fd_tpool_private_split( tpool_cnt );
     417             : 
     418           0 :     int err0; int dirty0;
     419           0 :     int err1; int dirty1;
     420             : 
     421           0 :     fd_tpool_exec( tpool, tpool_ts, fd_wksp_private_restore_v2_node,
     422           0 :                    tpool, tpool_ts, tpool_t1, _wksp, _restore, _hdr, _cgroup_frame_off, _cgroup_pinfo_lo, _cgroup_nxt, cgroup_cnt,
     423           0 :                    (ulong)&err1, (ulong)&dirty1 );
     424           0 :     fd_wksp_private_restore_v2_node(
     425           0 :                    tpool, tpool_t0, tpool_ts, _wksp, _restore, _hdr, _cgroup_frame_off, _cgroup_pinfo_lo, _cgroup_nxt, cgroup_cnt,
     426           0 :                    (ulong)&err0, (ulong)&dirty0 );
     427           0 :     fd_tpool_wait( tpool, tpool_ts );
     428             : 
     429           0 :     *(int *)_err   = fd_int_if( !!err0, err0, err1 ); /* Return first error encountered */
     430           0 :     *(int *)_dirty = dirty0 | dirty1;                 /* Accumulate the dirty flag */
     431           0 :     return;
     432           0 :   }
     433             : 
     434             :   /* This node is responsible for a single thread.  Unpack the input
     435             :      arguments. */
     436             : 
     437          63 :   fd_wksp_t *                      wksp             = (fd_wksp_t *)               _wksp;
     438          63 :   fd_restore_t *                   restore          = (fd_restore_t *)            _restore; /* FIXME: CLONE RESTORE */
     439          63 :   fd_wksp_checkpt_v2_hdr_t const * hdr              = (fd_wksp_checkpt_v2_hdr_t *)_hdr;
     440          63 :   ulong const *                    cgroup_frame_off = (ulong *)                   _cgroup_frame_off;
     441          63 :   ulong const *                    cgroup_pinfo_lo  = (ulong *)                   _cgroup_pinfo_lo;
     442             : 
     443          63 :   int err   = FD_WKSP_SUCCESS;
     444          63 :   int dirty = 0;
     445             : 
     446             :   /* Since we can't have multiple threads operate concurrently on the
     447             :      same restore object, make a new restore object safe for use by this
     448             :      thread (technically could use restore directly on original thread
     449             :      t0). */
     450             : 
     451          63 :   fd_restore_t _restore_local[1];
     452          63 :   fd_restore_t * restore_local =
     453          63 :     fd_restore_init_mmio( _restore_local, fd_restore_mmio( restore ), fd_restore_mmio_sz( restore ) ); /* logs details */
     454          63 :   if( FD_UNLIKELY( !restore_local ) ) {
     455           0 :     err = FD_WKSP_ERR_FAIL;
     456           0 :     goto done;
     457           0 :   }
     458             : 
     459          84 :   for(;;) {
     460             : 
     461             :     /* Get the next cgroup to restore.  Use a dynamic task queue model
     462             :        here because we assume that restore a single cgroups requires a
     463             :        large amount of work and the amount of work is highly variable.
     464             :        Note that using an atomic increment for the cgroup_nxt counter
     465             :        assumes:
     466             : 
     467             :          cgroup_cnt << ULONG_MAX - TILE_MAX.
     468             : 
     469             :        We could use a slower atomic CAS based version instead if we want
     470             :        to insure that cgroup_nxt is never incremented beyond cgroup_cnt.
     471             :        We could also use a block partitioning or CUDA style striping if
     472             :        wanting to do a deterministic distribution but these might not
     473             :        load balance as well in various extreme circumstances. */
     474             : 
     475          84 : #   if FD_HAS_ATOMIC
     476          84 :     FD_COMPILER_MFENCE();
     477          84 :     ulong cgroup_idx = FD_ATOMIC_FETCH_AND_ADD( (ulong *)_cgroup_nxt, 1UL );
     478          84 :     FD_COMPILER_MFENCE();
     479             : #   else /* Note: this assumes platforms without HAS_ATOMIC will not be running this multithreaded */
     480             :     ulong cgroup_idx = (*(ulong *)_cgroup_nxt) + 1UL;
     481             : #   endif
     482             : 
     483          84 :     if( FD_UNLIKELY( cgroup_idx>=cgroup_cnt ) ) break; /* No more cgroups to process */
     484             : 
     485             :     /* Restore this cgroup */
     486             : 
     487          21 :     int dirty_cgroup;
     488          21 :     err = fd_wksp_private_restore_v2_cgroup( wksp, restore_local, hdr,
     489          21 :                                              cgroup_frame_off[ cgroup_idx ], cgroup_frame_off[ cgroup_idx+1UL ],
     490          21 :                                              cgroup_pinfo_lo [ cgroup_idx ], cgroup_pinfo_lo [ cgroup_idx+1UL ],
     491          21 :                                              &dirty_cgroup ); /* logs details */
     492          21 :     dirty |= dirty_cgroup;
     493          21 :     if( FD_UNLIKELY( err ) ) break; /* abort if we encountered an error */
     494             : 
     495          21 :   }
     496             : 
     497          63 :   fd_restore_fini( restore_local );
     498             : 
     499          63 : done:
     500          63 :   *(int *)_err   = err;
     501          63 :   *(int *)_dirty = dirty;
     502          63 : }
     503             : 
     504             : /* fd_wksp_private_restore_v2_mmio replaces all the allocations in a
     505             :    wksp with the allocations in the restore.  Assumes all inputs have
     506             :    are valid, restore is positioned on the first byte of the header, has
     507             :    the given size and is seekable.  Returns SUCCESS on success and the
     508             :    restore will be positioned just after the footer.  Returns FAIL if an
     509             :    error occurred before wksp was not modified and CORRUPT if an error
     510             :    occurred after.  On failure, the restore state is indeterminant.
     511             :    Uses tpool threads [t0,t1) to do the restore.  Assumes the caller is
     512             :    thread t0 and threads (t0,t1) are available for dispatch. */
     513             : 
     514             : static int
     515             : fd_wksp_private_restore_v2_mmio( fd_tpool_t *   tpool,
     516             :                                  ulong          t0,
     517             :                                  ulong          t1,
     518             :                                  fd_wksp_t *    wksp,
     519             :                                  fd_restore_t * restore,
     520          63 :                                  uint           new_seed ) {
     521             : 
     522          63 :   ulong frame_off;
     523             : 
     524          63 :   int locked = 0; /* is the wksp currently locked? */
     525          63 :   int dirty  = 0; /* has the wksp been modified? */
     526             : 
     527             :   /* Restore and validate the header, info, and footer.  In principle
     528             :      this could be parallelized but probably not worth it. */
     529             : 
     530          63 :   ulong restore_sz = fd_restore_sz( restore );
     531             : 
     532          63 :   ulong frame_off_hdr  = 0UL;
     533          63 :   ulong frame_off_info = frame_off_hdr + sizeof(fd_wksp_checkpt_v2_hdr_t);
     534          63 :   ulong frame_off_ftr  = restore_sz    - sizeof(fd_wksp_checkpt_v2_ftr_t);
     535             : 
     536          63 :   RESTORE_TEST( /*(0UL<=frame_off_hdr) &*/ (frame_off_hdr<frame_off_info) & (frame_off_info<frame_off_ftr) & (frame_off_ftr<restore_sz) );
     537             : 
     538          63 :   fd_wksp_checkpt_v2_hdr_t hdr[1];
     539             : 
     540             : //RESTORE_SEEK( frame_off_hdr );
     541          63 :   RESTORE_TEST( !fd_wksp_private_restore_v2_common( hdr, restore ) );
     542             : 
     543          63 :   FD_LOG_INFO(( "Restoring footer" ));
     544             : 
     545          63 :   fd_wksp_checkpt_v2_ftr_t ftr[1];
     546             : 
     547          63 :   RESTORE_SEEK( frame_off_ftr );
     548          63 :   RESTORE_TEST( !fd_wksp_restore_v2_ftr( restore, hdr, ftr, restore_sz ) );
     549             : 
     550          63 :   ulong frame_off_volumes = ftr->frame_off;
     551             : 
     552          63 :   RESTORE_TEST( (frame_off_info<frame_off_volumes) & (frame_off_volumes<frame_off_ftr) );
     553             : 
     554          63 :   if( FD_UNLIKELY( ftr->alloc_cnt>wksp->part_max ) ) {
     555           0 :     FD_LOG_WARNING(( "restore failed because there are too few wksp partitions to restore allocations into "
     556           0 :                      "(ftr alloc_cnt %lu, hdr part_max %lu, wksp part_max %lu)",
     557           0 :                      ftr->alloc_cnt, hdr->part_max, wksp->part_max ));
     558           0 :     goto fail;
     559           0 :   }
     560             : 
     561          63 :   FD_LOG_INFO(( "Restoring volumes" ));
     562             : 
     563          63 :   fd_wksp_checkpt_v2_cmd_t cmd[1];
     564             : 
     565          63 :   RESTORE_SEEK( frame_off_volumes );
     566          63 :   RESTORE_OPEN( hdr->frame_style_compressed );
     567          63 :   RESTORE_META( cmd, sizeof(fd_wksp_checkpt_v2_cmd_t) );
     568          63 :   RESTORE_CLOSE();
     569             : 
     570          63 :   RESTORE_TEST( (cmd->volumes.tag==0UL) & (cmd->volumes.cgroup_cnt==ULONG_MAX) ); /* frame_off_appendix tested below */
     571          63 :   RESTORE_TEST( (frame_off_volumes<frame_off) & (frame_off<=frame_off_ftr) );     /* ==frame_off_ftr if compactly stored */
     572             : 
     573          63 :   FD_LOG_INFO(( "Locking wksp" ));
     574             : 
     575          63 :   if( FD_UNLIKELY( fd_wksp_private_lock( wksp ) ) ) goto fail; /* logs details */
     576          63 :   locked = 1;
     577             : 
     578             :   /* For all volumes */
     579             : 
     580          63 :   ulong alloc_rem  = ftr->alloc_cnt;  /* Number of allocations remaining to process */
     581          63 :   ulong cgroup_rem = ftr->cgroup_cnt; /* Number of cgroups     remaining to process */
     582          63 :   ulong volume_rem = ftr->volume_cnt; /* Number of volumes     remaining to process */
     583             : 
     584          63 :   ulong frame_off_volume_lo = frame_off_info;
     585          63 :   ulong frame_off_volume_hi = frame_off_volumes;
     586          63 :   ulong frame_off_appendix  = cmd->volumes.frame_off;
     587             : 
     588         126 :   while( frame_off_appendix ) {
     589             : 
     590             :     /* Verify we still have volumes remaining and the appendix location
     591             :        is between the info frame and the next volume (or the footer if
     592             :        the last volume) */
     593             : 
     594          63 :     RESTORE_TEST( (volume_rem>0UL) & (frame_off_volume_lo<frame_off_appendix) & (frame_off_appendix<frame_off_volume_hi) );
     595             : 
     596             :     /* Now that we know where this volume's appendix is supposed to be,
     597             :        seek to it and then restore and validate it. */
     598             : 
     599          63 :     FD_LOG_INFO(( "Restoring volume appendix" ));
     600             : 
     601          63 :     RESTORE_SEEK( frame_off_appendix );
     602             : 
     603          63 :     ulong cgroup_frame_off[ FD_WKSP_RESTORE_V2_CGROUP_MAX+1UL ];
     604          63 :     ulong cgroup_pinfo_lo [ FD_WKSP_RESTORE_V2_CGROUP_MAX+1UL ];
     605          63 :     ulong cgroup_cnt;
     606             : 
     607          63 :     ulong frame_off_prev;
     608             : 
     609          63 :     {
     610          63 :       RESTORE_OPEN( hdr->frame_style_compressed );
     611             : 
     612          63 :       fd_wksp_checkpt_v2_cmd_t cmd[1];
     613             : 
     614          63 :       RESTORE_META( cmd, sizeof(fd_wksp_checkpt_v2_cmd_t) );
     615          63 :       RESTORE_TEST( fd_wksp_checkpt_v2_cmd_is_appendix( cmd ) );
     616             : 
     617          63 :       cgroup_cnt     = cmd->appendix.cgroup_cnt;
     618          63 :       frame_off_prev = cmd->appendix.frame_off;
     619             : 
     620          63 :       if( FD_UNLIKELY( cgroup_cnt>FD_WKSP_RESTORE_V2_CGROUP_MAX ) ) {
     621           0 :         FD_LOG_WARNING(( "increase FD_WKSP_RESTORE_V2_CGROUP_MAX for this target" ));
     622           0 :         goto fail;
     623           0 :       }
     624             : 
     625          63 :       RESTORE_DATA( cgroup_frame_off, cgroup_cnt*sizeof(ulong) );
     626          63 :       RESTORE_DATA( cgroup_pinfo_lo,  cgroup_cnt*sizeof(ulong) ); /* cgroup_alloc_cnt now, pinfo cgroup partitioning later */
     627          63 :       RESTORE_CLOSE();
     628             : 
     629             :       /* Verify this cgroups frames are between the previous appendix frame
     630             :          (or the info frame if the first volume) and this appendix frame
     631             :          and ordered.  Also, verify the cgroup allocation counts,
     632             :          convert the counts into a partitioning of wksp's pinfo array
     633             :          and make sure we have enough partitions in the wksp to attempt
     634             :          the restore.  In principle, this loop could be parallelized but
     635             :          probably not worth it. */
     636             : 
     637          63 :       cgroup_frame_off[ cgroup_cnt ] = frame_off_appendix;
     638          63 :       cgroup_pinfo_lo [ cgroup_cnt ] = alloc_rem;
     639             : 
     640          84 :       for( ulong cgroup_rem=cgroup_cnt; cgroup_rem; cgroup_rem-- ) {
     641             : 
     642          21 :         ulong cgroup_idx = cgroup_rem - 1UL;
     643          21 :         RESTORE_TEST( cgroup_frame_off[ cgroup_idx ] < cgroup_frame_off[ cgroup_idx+1UL ] );
     644             : 
     645          21 :         ulong cgroup_alloc_cnt = cgroup_pinfo_lo[ cgroup_idx ];
     646          21 :         RESTORE_TEST( cgroup_alloc_cnt<=alloc_rem );
     647          21 :         alloc_rem -= cgroup_alloc_cnt;
     648          21 :         cgroup_pinfo_lo[ cgroup_idx ] = alloc_rem;
     649             : 
     650          21 :       }
     651             : 
     652          63 :       RESTORE_TEST( fd_ulong_max( frame_off_prev, frame_off_info ) < cgroup_frame_off[0] );
     653          63 :     }
     654             : 
     655             :     /* At this point, we know how to do an embarassingly parallel
     656             :        restore directly into the wksp.  Dispatch work to tpool threads
     657             :        [t0,t1).  This assumes we are tpool thread t0 and threads (t0,t1)
     658             :        are available for dispatch.  On return from the dispatch, err
     659             :        will contain the error code from the lowest indexed cgroup_idx
     660             :        that encountered an error (if any error was encountered, some
     661             :        cgroups might not have been processed) and dirty_node will
     662             :        contain non-zero if the wksp was modified. */
     663             : 
     664          63 :     FD_LOG_INFO(( "Restoring volume cgroups" ));
     665             : 
     666          63 :     ulong cgroup_nxt[1];
     667             : 
     668          63 :     FD_COMPILER_MFENCE();
     669          63 :     FD_VOLATILE( cgroup_nxt[0] ) = 0UL;
     670          63 :     FD_COMPILER_MFENCE();
     671             : 
     672          63 :     int err;
     673          63 :     int dirty_node;
     674          63 :     fd_wksp_private_restore_v2_node( (void *)tpool, t0, t1,
     675          63 :                                      (void *)wksp, (void *)restore, (ulong)hdr, (ulong)cgroup_frame_off, (ulong)cgroup_pinfo_lo,
     676          63 :                                      (ulong)cgroup_nxt, cgroup_cnt, (ulong)&err, (ulong)&dirty_node );
     677          63 :     dirty |= dirty_node;
     678          63 :     if( FD_UNLIKELY( err ) ) goto fail;
     679             : 
     680             :     /* Advance to the next volume */
     681             : 
     682          63 :     cgroup_rem -= cgroup_cnt;
     683          63 :     volume_rem--;
     684             :     /* frame_off_volume_lo unchanged */
     685          63 :     frame_off_volume_hi = cgroup_frame_off[ 0 ];
     686          63 :     frame_off_appendix  = frame_off_prev;
     687          63 :   }
     688             : 
     689             :   /* Make sure we got all volumes and all cgroups and position the
     690             :      restore at the location it would have been at in a streaming
     691             :      restore. */
     692             : 
     693          63 :   RESTORE_TEST( alloc_rem ==0UL );
     694          63 :   RESTORE_TEST( cgroup_rem==0UL );
     695          63 :   RESTORE_TEST( volume_rem==0UL );
     696             : 
     697          63 :   RESTORE_SEEK( restore_sz );
     698             : 
     699             :   /* Free any remaining old allocations and rebuild the wksp with our
     700             :      freshly restored allocations.  In principle the free loop can be
     701             :      parallelized but it is probably not worth it. */
     702             : 
     703          63 :   FD_LOG_INFO(( "Rebuilding wksp" ));
     704             : 
     705          63 :   dirty = 1;
     706             : 
     707          63 :   fd_wksp_private_pinfo_t * pinfo    = fd_wksp_private_pinfo( wksp );
     708          63 :   ulong                     part_max = wksp->part_max;
     709             : 
     710     1031172 :   for( ulong part_idx=ftr->alloc_cnt; part_idx<part_max; part_idx++ ) pinfo[ part_idx ].tag = 0UL;
     711             : 
     712          63 :   if( FD_UNLIKELY( fd_wksp_rebuild( wksp, new_seed ) ) ) goto fail; /* logs details */
     713             : 
     714             : # if FD_HAS_DEEPASAN
     715             :   fd_wksp_private_asan_sync( wksp ); /* old allocations are gone now */
     716             : # endif
     717             : 
     718          63 :   FD_LOG_INFO(( "Unlocking wksp" ));
     719             : 
     720          63 :   fd_wksp_private_unlock( wksp );
     721             : 
     722          63 :   return FD_WKSP_SUCCESS;
     723             : 
     724           0 : fail: /* Release resources that might be reserved */
     725             : 
     726           0 :   if( FD_LIKELY( locked ) ) fd_wksp_private_unlock( wksp );
     727             : 
     728           0 :   return fd_int_if( dirty, FD_WKSP_ERR_CORRUPT, FD_WKSP_ERR_FAIL );
     729          63 : }
     730             : 
     731             : /* fd_wksp_private_restore_v2_stream is identical to
     732             :    fd_wksp_private_restore_v2_mmio (above) but usable when restore is
     733             :    not using memory mapped i/o under the hood.  This includes when the
     734             :    restore is from a non-seekable file descriptor (e.g. when the restore
     735             :    is from a pipe or socket but this will work fine if used on mmio
     736             :    restores too).  Restore must be compactly stored.  Exact same
     737             :    behaviors. */
     738             : 
     739             : static int
     740             : fd_wksp_private_restore_v2_stream( fd_wksp_t *    wksp,
     741             :                                    fd_restore_t * restore,
     742           0 :                                    uint           new_seed ) {
     743           0 :   ulong frame_off;
     744             : 
     745           0 :   int locked = 0; /* is the wksp currently locked */
     746           0 :   int dirty  = 0; /* has the wksp been modified? */
     747             : 
     748           0 :   fd_wksp_checkpt_v2_hdr_t hdr[1];
     749             : 
     750           0 :   RESTORE_TEST( !fd_wksp_private_restore_v2_common( hdr, restore ) );
     751             : 
     752           0 :   FD_LOG_INFO(( "Locking wksp" ));
     753             : 
     754           0 :   if( FD_UNLIKELY( fd_wksp_private_lock( wksp ) ) ) goto fail; /* logs details */
     755           0 :   locked = 1;
     756             : 
     757           0 :   fd_wksp_private_pinfo_t * pinfo    = fd_wksp_private_pinfo( wksp );
     758           0 :   ulong                     part_max = wksp->part_max;
     759           0 :   ulong                     data_max = wksp->data_max;
     760           0 :   ulong                     data_lo  = wksp->gaddr_lo;
     761           0 :   ulong                     data_hi  = wksp->gaddr_hi;
     762             : 
     763           0 :   ulong hdr_data_max = hdr->data_max;
     764           0 :   ulong hdr_data_lo  = fd_wksp_private_data_off( hdr->part_max );
     765           0 :   ulong hdr_data_hi  = hdr_data_lo + hdr_data_max;
     766             : 
     767             :   /* For all volumes in the checkpt */
     768             : 
     769           0 :   ulong ftr_alloc_cnt  = 0UL;
     770           0 :   ulong ftr_cgroup_cnt = 0UL;
     771           0 :   ulong ftr_volume_cnt = 0UL;
     772           0 :   ulong frame_off_prev = 0UL;
     773             : 
     774           0 :   for(;;) {
     775             : 
     776           0 :     FD_LOG_INFO(( "Restoring volume %lu", ftr_volume_cnt ));
     777             : 
     778           0 :     ulong vol_cgroup_cnt = 0UL;
     779             : 
     780           0 :     ulong vol_cgroup_frame_off[ FD_WKSP_RESTORE_V2_CGROUP_MAX ];
     781           0 :     ulong vol_cgroup_alloc_cnt[ FD_WKSP_RESTORE_V2_CGROUP_MAX ];
     782             : 
     783           0 :     ulong vol_appendix_frame_off[ FD_WKSP_RESTORE_V2_CGROUP_MAX ];
     784           0 :     ulong vol_appendix_alloc_cnt[ FD_WKSP_RESTORE_V2_CGROUP_MAX ];
     785             : 
     786             :     /* For all cgroups in the volume */
     787             : 
     788           0 :     for(;;) {
     789             : 
     790           0 :       ulong part_lo = ftr_alloc_cnt;
     791             : 
     792             :       /* Open the frame and read the leading command to determine if the
     793             :          frame is a cgroup, appendix (which ends the volume) or an end
     794             :          of volumes frame (which ends the checkpt).  If it is an
     795             :          appendix, validate and close the frame and proceed to the next
     796             :          volume.  If it is the end of volumes, validate and close the
     797             :          frame and proceed to footer processing.  Otherwise, proceed to
     798             :          processing a cgroup frame. */
     799             : 
     800           0 :       RESTORE_OPEN( hdr->frame_style_compressed );
     801             : 
     802           0 :       fd_wksp_checkpt_v2_cmd_t cmd[1];
     803             : 
     804           0 :       RESTORE_META( cmd, sizeof(fd_wksp_checkpt_v2_cmd_t) );
     805             : 
     806           0 :       if( FD_UNLIKELY( fd_wksp_checkpt_v2_cmd_is_appendix( cmd ) ) ) {
     807           0 :         RESTORE_TEST( cmd->appendix.frame_off==frame_off_prev );
     808           0 :         frame_off_prev = frame_off;
     809             : 
     810           0 :         RESTORE_DATA( vol_appendix_frame_off, vol_cgroup_cnt*sizeof(ulong) );
     811           0 :         RESTORE_DATA( vol_appendix_alloc_cnt, vol_cgroup_cnt*sizeof(ulong) );
     812           0 :         RESTORE_CLOSE();
     813             : 
     814           0 :         RESTORE_TEST( !memcmp( vol_appendix_frame_off, vol_cgroup_frame_off, vol_cgroup_cnt*sizeof(ulong) ) );
     815           0 :         RESTORE_TEST( !memcmp( vol_appendix_alloc_cnt, vol_cgroup_alloc_cnt, vol_cgroup_cnt*sizeof(ulong) ) );
     816             : 
     817           0 :         break;
     818           0 :       }
     819             : 
     820           0 :       if( FD_UNLIKELY( fd_wksp_checkpt_v2_cmd_is_volumes( cmd ) ) ) {
     821           0 :         RESTORE_TEST( cmd->volumes.frame_off==frame_off_prev );
     822           0 :         frame_off_prev = frame_off;
     823             : 
     824           0 :         RESTORE_CLOSE();
     825             : 
     826           0 :         goto restore_footer;
     827           0 :       }
     828             : 
     829             :       /* At this point, we have read the leading command of a cgroup frame.
     830             :          Restore the cgroup allocation metadata. */
     831             : 
     832           0 :       if( FD_UNLIKELY( vol_cgroup_cnt>=FD_WKSP_RESTORE_V2_CGROUP_MAX ) ) {
     833           0 :         FD_LOG_WARNING(( "increase FD_WKSP_RESTORE_V2_CGROUP_MAX" ));
     834           0 :         goto fail;
     835           0 :       }
     836             : 
     837           0 :       vol_cgroup_frame_off[ vol_cgroup_cnt ] = frame_off;
     838             : 
     839           0 :       for(;;) {
     840           0 :         if( FD_UNLIKELY( fd_wksp_checkpt_v2_cmd_is_data( cmd ) ) ) break;
     841           0 :         RESTORE_TEST( fd_wksp_checkpt_v2_cmd_is_meta( cmd ) );
     842             : 
     843           0 :         ulong tag      = cmd->meta.tag;      /* non-zero */
     844           0 :         ulong gaddr_lo = cmd->meta.gaddr_lo;
     845           0 :         ulong gaddr_hi = cmd->meta.gaddr_hi;
     846             : 
     847           0 :         RESTORE_TEST( (hdr_data_lo<=gaddr_lo) & (gaddr_lo<gaddr_hi) & (gaddr_hi<=hdr_data_hi) );
     848             :         /* Note: disjoint [gaddr_lo,gaddr_hi) tested on rebuild */
     849             : 
     850           0 :         if( FD_UNLIKELY( !((data_lo<=gaddr_lo) & (gaddr_hi<=data_hi)) ) ) {
     851           0 :           FD_LOG_WARNING(( "restore failed because checkpt allocation [0x%016lx,0x%016lx) tag %lu does not fit into the wksp "
     852           0 :                            "data region [0x%016lx,0x%016lx) (hdr_data_max %lu, wksp_data_max %lu)",
     853           0 :                            gaddr_lo, gaddr_hi, tag, data_lo, data_hi, hdr_data_max, data_max ));
     854           0 :           goto fail;
     855           0 :         }
     856             : 
     857           0 :         if( FD_UNLIKELY( ftr_alloc_cnt>=part_max ) ) {
     858           0 :           FD_LOG_WARNING(( "restore failed because there are too few wksp partitions to restore allocations into "
     859           0 :                            "(alloc_cnt %lu, hdr_part_max %lu, wksp_part_max %lu)",
     860           0 :                            ftr_alloc_cnt, hdr->part_max, wksp->part_max ));
     861           0 :           goto fail;
     862           0 :         }
     863             : 
     864           0 :         dirty = 1;
     865           0 :         pinfo[ ftr_alloc_cnt ].gaddr_lo = gaddr_lo;
     866           0 :         pinfo[ ftr_alloc_cnt ].gaddr_hi = gaddr_hi;
     867           0 :         pinfo[ ftr_alloc_cnt ].tag      = tag;
     868           0 :         ftr_alloc_cnt++;
     869             : 
     870           0 :         RESTORE_META( cmd, sizeof(fd_wksp_checkpt_v2_cmd_t) );
     871           0 :       }
     872             : 
     873             :       /* At this point, we have restored all cgroup allocation metadata
     874             :          into the pinfo array at [part_lo,ftr_alloc_cnt).  Restore the
     875             :          corresponding cgroup allocation data. */
     876             : 
     877           0 :       for( ulong part_idx=part_lo; part_idx<ftr_alloc_cnt; part_idx++ ) {
     878           0 :         ulong gaddr_lo = pinfo[ part_idx ].gaddr_lo;
     879           0 :         ulong gaddr_hi = pinfo[ part_idx ].gaddr_hi;
     880             : 
     881           0 :         dirty = 1;
     882             : #       if FD_HAS_DEEPASAN
     883             :         /* The destination can be poisoned free space, so unpoison it
     884             :            before restoring into it. */
     885             :         fd_asan_unpoison( fd_wksp_laddr_fast( wksp, gaddr_lo ), gaddr_hi - gaddr_lo );
     886             : #       endif
     887           0 :         RESTORE_DATA( fd_wksp_laddr_fast( wksp, gaddr_lo ), gaddr_hi - gaddr_lo );
     888           0 :       }
     889             : 
     890             :       /* Close the cgroup frame */
     891             : 
     892           0 :       RESTORE_CLOSE();
     893             : 
     894             :       /* Update verification info */
     895             : 
     896           0 :       vol_cgroup_alloc_cnt[ vol_cgroup_cnt ] = ftr_alloc_cnt - part_lo;
     897           0 :       vol_cgroup_cnt++;
     898             : 
     899           0 :     }
     900             : 
     901             :     /* Update verification info */
     902             : 
     903           0 :     ftr_cgroup_cnt += vol_cgroup_cnt;
     904           0 :     ftr_volume_cnt++;
     905           0 :   }
     906             : 
     907           0 : restore_footer:
     908             : 
     909             :   /* At this point, the checkpt is positioned at the start of the
     910             :      footer.  Restore and validate it.  Note that checkpt data has been
     911             :      fully decompressed into the wksp pinfo and data region but the wksp
     912             :      indexing structures have not been rebuilt.  Further note that
     913             :      restoring the footer is pure validation. */
     914             : 
     915           0 :   FD_LOG_INFO(( "Restoring footer" ));
     916             : 
     917           0 :   fd_wksp_checkpt_v2_ftr_t ftr[1];
     918             : 
     919           0 :   RESTORE_TEST( !fd_wksp_restore_v2_ftr( restore, hdr, ftr, frame_off + sizeof(fd_wksp_checkpt_v2_ftr_t) ) );
     920             : 
     921           0 :   RESTORE_TEST( ftr->alloc_cnt ==ftr_alloc_cnt  );
     922           0 :   RESTORE_TEST( ftr->cgroup_cnt==ftr_cgroup_cnt );
     923           0 :   RESTORE_TEST( ftr->volume_cnt==ftr_volume_cnt );
     924           0 :   RESTORE_TEST( ftr->frame_off ==frame_off_prev );
     925             : 
     926           0 :   FD_LOG_INFO(( "Rebuilding wksp" ));
     927             : 
     928             :   /* Free any remaining old allocations and rebuild the wksp with
     929             :      the freshly restored allocations */
     930             : 
     931           0 :   dirty = 1;
     932           0 :   for( ulong part_idx=ftr_alloc_cnt; part_idx<part_max; part_idx++ ) pinfo[ part_idx ].tag = 0UL;
     933             : 
     934           0 :   if( FD_UNLIKELY( fd_wksp_rebuild( wksp, new_seed ) ) ) goto fail; /* logs details */
     935             : 
     936             : # if FD_HAS_DEEPASAN
     937             :   fd_wksp_private_asan_sync( wksp ); /* old allocations are gone now */
     938             : # endif
     939             : 
     940           0 :   FD_LOG_INFO(( "Unlocking wksp" ));
     941             : 
     942           0 :   fd_wksp_private_unlock( wksp );
     943             : 
     944           0 :   return FD_WKSP_SUCCESS;
     945             : 
     946           0 : fail: /* Release resources that might be reserved */
     947             : 
     948           0 :   if( FD_LIKELY( locked ) ) fd_wksp_private_unlock( wksp );
     949             : 
     950           0 :   return fd_int_if( dirty, FD_WKSP_ERR_CORRUPT, FD_WKSP_ERR_FAIL );
     951           0 : }
     952             : 
     953             : int
     954             : fd_wksp_private_restore_v2( fd_tpool_t * tpool,
     955             :                             ulong        t0,
     956             :                             ulong        t1,
     957             :                             fd_wksp_t *  wksp,
     958             :                             char const * path,
     959          63 :                             uint         new_seed ) {
     960             : 
     961          63 :   FD_LOG_INFO(( "Restoring checkpt \"%s\" into wksp \"%s\" (seed %u)", path, wksp->name, new_seed ));
     962             : 
     963          63 :   int            fd      = -1;
     964          63 :   void const *   mmio    = NULL;
     965          63 :   ulong          mmio_sz = 0UL;
     966          63 :   fd_restore_t * restore = NULL;
     967             : 
     968          63 :   fd_restore_t   _restore[ 1 ];
     969          63 :   uchar          rbuf[ FD_RESTORE_RBUF_MIN ];
     970             : 
     971          63 :   FD_LOG_INFO(( "Opening checkpt" ));
     972             : 
     973          63 :   fd = open( path, O_RDONLY, (mode_t)0 );
     974          63 :   if( FD_UNLIKELY( fd==-1 ) ) {
     975           0 :     FD_LOG_WARNING(( "open(\"%s\",O_RDONLY,0) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     976           0 :     goto fail;
     977           0 :   }
     978             : 
     979          63 :   int err = fd_io_mmio_init( fd, FD_IO_MMIO_MODE_READ_ONLY, &mmio, &mmio_sz );
     980          63 :   if( FD_LIKELY( !err ) ) {
     981             : 
     982          63 :     FD_LOG_INFO(( "Restoring checkpt with mmio" ));
     983             : 
     984             :     /* FIXME: consider trimming off prefix / suffix here (i.e. scan for
     985             :        MAGIC / ~MAGIC) */
     986             : 
     987          63 :     restore = fd_restore_init_mmio( _restore, mmio, mmio_sz ); /* logs details */
     988          63 :     if( FD_UNLIKELY( !restore ) ) goto fail;
     989             : 
     990          63 :     err = fd_wksp_private_restore_v2_mmio( tpool, t0, t1, wksp, restore, new_seed ); /* logs details */
     991          63 :     if( FD_UNLIKELY( err ) ) goto fail;
     992             : 
     993          63 :   } else {
     994             : 
     995           0 :     FD_LOG_INFO(( "\"%s\" does not appear to support mmio (%i-%s); restoring checkpt with streaming",
     996           0 :                   path, err, fd_io_strerror( err ) ));
     997             : 
     998             :     /* FIXME: consider trimming off prefix (i.e. scan for MAGIC) here */
     999             : 
    1000           0 :     restore = fd_restore_init_stream( _restore, fd, rbuf, FD_RESTORE_RBUF_MIN ); /* logs details */
    1001           0 :     if( FD_UNLIKELY( !restore ) ) goto fail;
    1002             : 
    1003           0 :     err = fd_wksp_private_restore_v2_stream( wksp, restore, new_seed ); /* logs details */
    1004           0 :     if( FD_UNLIKELY( err ) ) goto fail;
    1005             : 
    1006           0 :   }
    1007             : 
    1008          63 :   FD_LOG_INFO(( "Closing checkpt" ));
    1009             : 
    1010          63 :   if( FD_UNLIKELY( !fd_restore_fini( restore ) ) ) /* logs details */
    1011           0 :     FD_LOG_WARNING(( "fd_restore_fini failed; attempting to continue" ));
    1012             : 
    1013          63 :   if( FD_LIKELY( mmio_sz ) ) fd_io_mmio_fini( mmio, mmio_sz );
    1014             : 
    1015          63 :   if( FD_UNLIKELY( close( fd ) ) )
    1016           0 :     FD_LOG_WARNING(( "close(\"%s\") failed (%i-%s); attempting to continue", path, errno, fd_io_strerror( errno ) ));
    1017             : 
    1018          63 :   return err;
    1019             : 
    1020           0 : fail:
    1021             : 
    1022           0 :   if( FD_LIKELY( restore ) ) {
    1023           0 :     if( FD_UNLIKELY( fd_restore_in_frame( restore ) ) && FD_UNLIKELY( fd_restore_close( restore ) ) )
    1024           0 :       FD_LOG_WARNING(( "fd_restore_close failed; attempting to continue" ));
    1025             : 
    1026           0 :     if( FD_UNLIKELY( !fd_restore_fini( restore ) ) ) /* logs details */
    1027           0 :       FD_LOG_WARNING(( "fd_restore_fini failed; attempting to continue" ));
    1028           0 :   }
    1029             : 
    1030           0 :   if( FD_LIKELY( mmio_sz ) ) fd_io_mmio_fini( mmio, mmio_sz );
    1031             : 
    1032           0 :   if( FD_LIKELY( fd!=-1 ) && FD_UNLIKELY( close( fd ) ) )
    1033           0 :     FD_LOG_WARNING(( "close(\"%s\") failed (%i-%s); attempting to continue", path, errno, fd_io_strerror( errno ) ));
    1034             : 
    1035           0 :   return FD_WKSP_ERR_FAIL;
    1036          63 : }
    1037             : 
    1038             : int
    1039             : fd_wksp_private_printf_v2( int          out,
    1040             :                            char const * path,
    1041          63 :                            int          verbose ) {
    1042             : 
    1043          63 :   int ret = 0;
    1044         117 : # define TRAP(x) do { int _err = (x); if( FD_UNLIKELY( _err<0 ) ) { ret = _err; goto fail; } ret += _err; } while(0)
    1045             : 
    1046          63 :   int            fd      = -1;
    1047          63 :   fd_restore_t * restore = NULL;
    1048             : 
    1049          63 :   fd_restore_t _restore[ 1 ];
    1050          63 :   uchar        rbuf[ FD_RESTORE_RBUF_MIN ];
    1051             : 
    1052             :   /* Print the header and metadata */
    1053             : 
    1054          63 :   if( verbose>=1 ) {
    1055             : 
    1056             :      /* Open the restore */
    1057             : 
    1058          63 :     fd = open( path, O_RDONLY, (mode_t)0 );
    1059          63 :     if( FD_UNLIKELY( fd==-1 ) ) {
    1060           0 :       FD_LOG_WARNING(( "open(\"%s\",O_RDONLY,0) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
    1061           0 :       goto fail;
    1062           0 :     }
    1063             : 
    1064          63 :     restore = fd_restore_init_stream( _restore, fd, rbuf, FD_RESTORE_RBUF_MIN ); /* logs details */
    1065          63 :     if( FD_UNLIKELY( !restore ) ) goto fail;
    1066             : 
    1067             :     /* Restore the header */
    1068             : 
    1069          63 :     fd_wksp_checkpt_v2_hdr_t hdr[1];
    1070             : 
    1071          63 :     RESTORE_TEST( !fd_wksp_restore_v2_hdr( restore, hdr ) );
    1072             : 
    1073             :     /* Restore the info */
    1074             : 
    1075          63 :     fd_wksp_checkpt_v2_info_t info[1];
    1076          63 :     char                      info_buf[ 65536 ];
    1077          63 :     char const *              info_cstr[ 9 ];
    1078             : 
    1079          63 :     RESTORE_TEST( !fd_wksp_restore_v2_info( restore, hdr, info, info_buf, 65536UL, info_cstr ) );
    1080             : 
    1081          63 :     char info_wallclock[ FD_LOG_WALLCLOCK_CSTR_BUF_SZ ];
    1082          63 :     fd_log_wallclock_cstr( info->wallclock, info_wallclock );
    1083             : 
    1084             :     /* Pretty print the header and info */
    1085             : 
    1086          63 :     TRAP( dprintf( out,
    1087             :                  //"\tstyle                  %-20i\n"        /* verbose 0 info (already printed) */
    1088             :                  //"\tname                   %s\n"
    1089             :                  //"\tseed                   %-20u\n"
    1090             :                  //"\tpart_max               %-20lu\n"
    1091             :                  //"\tdata_max               %-20lu\n"
    1092          63 :                    "\tmagic                  %016lx\n"      /* verbose 1 info */
    1093          63 :                    "\twallclock              %-20li (%s)\n"
    1094          63 :                    "\tapp                    %-20lu (%s)\n"
    1095          63 :                    "\tthread                 %-20lu (%s)\n"
    1096          63 :                    "\thost                   %-20lu (%s)\n"
    1097          63 :                    "\tcpu                    %-20lu (%s)\n"
    1098          63 :                    "\tgroup                  %-20lu (%s)\n"
    1099          63 :                    "\ttid                    %-20lu\n"
    1100          63 :                    "\tuser                   %-20lu (%s)\n"
    1101          63 :                    "\tframe_style_compressed %-20i\n",      /* (v2 specific) */
    1102          63 :                    hdr->magic,
    1103          63 :                    info->wallclock, info_wallclock,
    1104          63 :                    info->app_id,    info_cstr[0],
    1105          63 :                    info->thread_id, info_cstr[1],
    1106          63 :                    info->host_id,   info_cstr[2],
    1107          63 :                    info->cpu_id,    info_cstr[3],
    1108          63 :                    info->group_id,  info_cstr[4],
    1109          63 :                    info->tid,
    1110          63 :                    info->user_id,   info_cstr[5],
    1111          63 :                    hdr->frame_style_compressed ) );
    1112             : 
    1113          63 :     if( verbose>=2 )
    1114          54 :       TRAP( dprintf( out, "\tmode                   %03lo\n" /* (v2 specific) */
    1115          63 :                           "\tpath\n\t\t%s\n"                 /* (v2 specific) */
    1116          63 :                           "\tbinfo\n\t\t%s\n"
    1117          63 :                           "\tuinfo\n\t\t%s\n",
    1118          63 :                           info->mode, info_cstr[6], info_cstr[7], info_cstr[8] ) );
    1119             : 
    1120             :     /* FIXME: consider implement handling of verbose>=3.  Since data in a
    1121             :        compressed frame can't be easily skipped over (due to sequential
    1122             :        dependencies between compressed data bufs inherently induced by
    1123             :        compression algos), we would:
    1124             : 
    1125             :        Use stat to get the size of the checkpt, seek to the end of the
    1126             :        file and restore the footer frame to get the appendix frame
    1127             :        location, seek to the appendix frame, and restore it to get the
    1128             :        cgroup frame offsets and partition counts.  Then, for each cgroup,
    1129             :        seek to the cgruop frame, init a streaming restore, open the frame,
    1130             :        restore the partition count and partition metadata (which is
    1131             :        conveniently located at the start of a cgroup frame), close it and
    1132             :        fini the restore.  Omitting for now as this isn't particularly
    1133             :        important functionality. */
    1134             : 
    1135             :     /* Finish restoring */
    1136             : 
    1137          63 :     if( FD_UNLIKELY( !fd_restore_fini( restore ) ) ) /* logs details */
    1138           0 :       FD_LOG_WARNING(( "fd_restore_fini failed; attempting to continue" ));
    1139             : 
    1140          63 :     if( FD_UNLIKELY( close( fd ) ) )
    1141           0 :       FD_LOG_WARNING(( "close failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
    1142          63 :   }
    1143             : 
    1144          63 : # undef TRAP
    1145             : 
    1146          63 :   return ret;
    1147             : 
    1148           0 : fail: /* Release resources that might be reserved */
    1149             : 
    1150           0 :   if( FD_LIKELY( restore ) ) {
    1151           0 :     if( FD_UNLIKELY( fd_restore_in_frame( restore ) ) && FD_UNLIKELY( fd_restore_close( restore ) ) )
    1152           0 :       FD_LOG_WARNING(( "fd_restore_close failed; attempting to continue" ));
    1153             : 
    1154           0 :     if( FD_UNLIKELY( !fd_restore_fini( restore ) ) ) /* logs details */
    1155           0 :       FD_LOG_WARNING(( "fd_restore_fini failed; attempting to continue" ));
    1156           0 :   }
    1157             : 
    1158           0 :   if( FD_LIKELY( fd!=-1 ) && FD_UNLIKELY( close( fd ) ) )
    1159           0 :     FD_LOG_WARNING(( "close failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
    1160             : 
    1161           0 :   return ret;
    1162          63 : }
    1163             : 
    1164             : #undef RESTORE_TEST
    1165             : #undef RESTORE_DATA
    1166             : #undef RESTORE_META
    1167             : #undef RESTORE_CLOSE
    1168             : #undef RESTORE_OPEN
    1169             : #undef RESTORE_SEEK

Generated by: LCOV version 1.14