LCOV - code coverage report
Current view: top level - util/wksp - fd_wksp_restore_v2.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 584 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 10 0.0 %

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

Generated by: LCOV version 1.14