LCOV - code coverage report
Current view: top level - util/wksp - fd_wksp_checkpt_v2.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 248 306 81.0 %
Date: 2026-11-12 04:25:03 Functions: 1 1 100.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 <errno.h>
       6             : #include <unistd.h>
       7             : #include <fcntl.h>
       8             : #include <sys/stat.h>
       9             : 
      10             : /* This is an implementation detail and not strictly part of the v2
      11             :    specification. */
      12             : 
      13          24 : #define FD_WKSP_CHECKPT_V2_CGROUP_MAX (1024UL)
      14             : 
      15             : int
      16             : fd_wksp_private_checkpt_v2( fd_tpool_t * tpool,
      17             :                             ulong        t0,
      18             :                             ulong        t1,
      19             :                             fd_wksp_t *  wksp,
      20             :                             char const * path,
      21             :                             ulong        mode,
      22             :                             char const * uinfo,
      23          24 :                             int          frame_style_compressed ) {
      24             : 
      25          24 :   (void)tpool; (void)t0; (void)t1; /* Thread parallelization not currently implemented */
      26             : 
      27          24 :   char const * binfo = fd_log_build_info;
      28             : 
      29          24 :   if( FD_UNLIKELY( !fd_checkpt_frame_style_is_supported( frame_style_compressed ) ) ) {
      30           0 :     FD_LOG_WARNING(( "compressed frames are not supported on this target" ));
      31           0 :     return FD_WKSP_ERR_INVAL;
      32           0 :   }
      33             : 
      34          24 :   int err_fail;
      35             : 
      36          24 :   int            locked  =  0;
      37          24 :   int            fd      = -1;
      38          24 :   fd_checkpt_t * checkpt = NULL;
      39             : 
      40          24 :   fd_wksp_private_pinfo_t * pinfo = fd_wksp_private_pinfo( wksp );
      41             : 
      42          24 :   char const * name     = wksp->name;
      43          24 :   ulong        name_len = fd_shmem_name_len( name );
      44          24 :   if( FD_UNLIKELY( !name_len ) ) {
      45           0 :     FD_LOG_WARNING(( "checkpt wksp to \"%s\" failed due to bad name; attempting to continue", path ));
      46           0 :     err_fail = FD_WKSP_ERR_CORRUPT;
      47           0 :     goto fail;
      48           0 :   }
      49             : 
      50             :   /* Lock the wksp */
      51             : 
      52          24 :   {
      53          24 :     int _err = fd_wksp_private_lock( wksp ); /* logs details */
      54          24 :     if( FD_UNLIKELY( _err ) ) {
      55           0 :       FD_LOG_WARNING(( "checkpt wksp \"%s\" to \"%s\" failed due to being locked; attempting to continue", name, path ));
      56           0 :       err_fail = _err;
      57           0 :       goto fail;
      58           0 :     }
      59          24 :     locked = 1;
      60          24 :   }
      61             : 
      62             :   /* Determine a reasonable number of cgroups (note: in principle we
      63             :      could thread parallelize this but it probably isn't worth the extra
      64             :      complexity). */
      65             : 
      66           0 :   ulong cgroup_cnt;
      67          24 :   ulong alloc_cnt = 0UL;
      68             : 
      69          24 :   {
      70         282 : #   define WKSP_TEST( c ) do {                                                                                  \
      71         282 :       if( FD_UNLIKELY( !(c) ) ) {                                                                               \
      72           0 :         FD_LOG_WARNING(( "checkpt wksp \"%s\" to \"%s\" failed due to failing test %s; attempting to continue", \
      73           0 :                          name, path, #c ));                                                                     \
      74           0 :         err_fail = FD_WKSP_ERR_CORRUPT;                                                                         \
      75           0 :         goto fail;                                                                                              \
      76           0 :       }                                                                                                         \
      77         282 :     } while(0)
      78             : 
      79             :     /* Count the number of allocations by traversing over all partitions
      80             :        in reverse order by gaddr_lo (same iteration we will do to assign
      81             :        partitions to cgroups), validating as we go. */
      82             : 
      83          24 :     ulong part_max  = wksp->part_max;
      84          24 :     ulong data_lo   = wksp->gaddr_lo;
      85          24 :     ulong data_hi   = wksp->gaddr_hi;
      86          24 :     ulong cycle_tag = wksp->cycle_tag++;
      87             : 
      88          24 :     WKSP_TEST( (0UL<data_lo) & (data_lo<=data_hi) ); /* Valid data region */
      89             : 
      90          24 :     ulong gaddr_last = data_hi;
      91             : 
      92          24 :     ulong part_idx = fd_wksp_private_pinfo_idx( wksp->part_tail_cidx );
      93         102 :     while( !fd_wksp_private_pinfo_idx_is_null( part_idx ) ) {
      94             : 
      95             :       /* Load partition metadata and validate it */
      96             : 
      97          78 :       WKSP_TEST( part_idx<part_max );                      /* Valid idx */
      98          78 :       WKSP_TEST( pinfo[ part_idx ].cycle_tag!=cycle_tag ); /* No cycles */
      99          78 :       pinfo[ part_idx ].cycle_tag = cycle_tag;             /* Mark part_idx as visited */
     100             : 
     101          78 :       ulong gaddr_lo = pinfo[ part_idx ].gaddr_lo;
     102          78 :       ulong gaddr_hi = pinfo[ part_idx ].gaddr_hi;
     103          78 :       ulong tag      = pinfo[ part_idx ].tag;
     104             : 
     105          78 :       WKSP_TEST( (data_lo<=gaddr_lo) & (gaddr_lo<gaddr_hi) & (gaddr_hi==gaddr_last) ); /* Valid partition range */
     106          78 :       gaddr_last = gaddr_lo;
     107             : 
     108             :       /* If this partition holds an allocation, count it */
     109             : 
     110          78 :       alloc_cnt += (ulong)(tag>0UL);
     111             : 
     112             :       /* Advance to the previous partition */
     113             : 
     114          78 :       part_idx = fd_wksp_private_pinfo_idx( pinfo[ part_idx ].prev_cidx );
     115          78 :     }
     116             : 
     117          24 :     WKSP_TEST( gaddr_last==data_lo ); /* Complete partitioning */
     118             : 
     119             :     /* Compute a reasonable cgroup_cnt for alloc_cnt.  To do this,
     120             :        let N be the number of allocations.  We assume they have IID
     121             :        sizes with mean U and standard deviation S.  If we assign each
     122             :        allocation to 1 of M cgroups IID uniform random (we will do
     123             :        better below but we pessimize here), in the limit of N>>M>>1, a
     124             :        cgroup's load (total number of allocation bytes assigned to a
     125             :        cgroup to compress) is Gaussian (by central limit theorem) with
     126             :        mean (N/M)U with standard deviation sqrt(N/M) sqrt(U^2+S^2).
     127             : 
     128             :        That is, we are load balanced on average (yay) but there is some
     129             :        natural imbalance expected due to statistical fluctuations (boo).
     130             :        Noting that allocation sizes are positive, if we further assume
     131             :        that S<~U typically (note that it is theoretically possible to
     132             :        have a positive valued random variable with S arbitrarily larger
     133             :        than U), then the cgroup load standard deviation is typically
     134             :        less than sqrt(2N/M) U.
     135             : 
     136             :        The load for each cgroup will be approximately independent of
     137             :        each other for M>>1.  Extremal value statistics for a Gaussian
     138             :        then implies that the least loaded cgroup is typically likely to
     139             :        have more than (N/M)U - sqrt(2N/M) U sqrt(2 ln M) load.  We want
     140             :        this to be positive such such that the least loaded cgroup will
     141             :        typically have some load:
     142             : 
     143             :             (N/M)U >> sqrt((4 N ln M)/M))U
     144             :          -> (N/M)  >> 4 ln M
     145             : 
     146             :        That is, we want pick the number of cgroups such that the number
     147             :        of allocations per cgroup on average much greater than a few
     148             :        times the natural log of the number of cgroups.
     149             : 
     150             :        Given the number of cgroups is at most CGROUP_MAX ~ 1024, the
     151             :        above implies if we target more than ~28 allocations per cgroup
     152             :        on average, each cgroup is likely to get some load and cgroups
     153             :        will be reasonably load balanced on average.  We use 32 below for
     154             :        computational convenience. */
     155             : 
     156          24 :     cgroup_cnt = fd_ulong_min( (alloc_cnt+31UL)/32UL, FD_WKSP_CHECKPT_V2_CGROUP_MAX );
     157             : 
     158          24 : #   undef WKSP_TEST
     159          24 :   }
     160             : 
     161             :   /* Assign allocations to cgroups (note: in principle we could thread
     162             :      parallelize this but it also probably isn't worth the extra
     163             :      complexity). */
     164             : 
     165           0 :   uint  cgroup_head_cidx[ FD_WKSP_CHECKPT_V2_CGROUP_MAX ]; /* Head of a linked list for partitions assigned to each cgroup */
     166          24 :   ulong cgroup_alloc_cnt[ FD_WKSP_CHECKPT_V2_CGROUP_MAX ]; /* Number of partitions in each cgroup */
     167             : 
     168          24 :   {
     169             : 
     170             :     /* Initialize the cgroups to empty */
     171             : 
     172          24 :     ulong cgroup_load[ FD_WKSP_CHECKPT_V2_CGROUP_MAX ];
     173             : 
     174          24 :     uint null_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
     175          36 :     for( ulong cgroup_idx=0UL; cgroup_idx<cgroup_cnt; cgroup_idx++ ) {
     176          12 :       cgroup_head_cidx[ cgroup_idx ] = null_cidx;
     177          12 :       cgroup_alloc_cnt[ cgroup_idx ] = 0UL;
     178          12 :       cgroup_load     [ cgroup_idx ] = 0UL;
     179          12 :     }
     180             : 
     181             :     /* Configure cgroup sampling */
     182             : 
     183          24 :     ulong cgroup_cursor = 0UL;
     184          24 :     ulong cgroup_idx    = 0UL;
     185             : 
     186             :     /* For all partitions in reverse order by gaddr_lo */
     187             : 
     188          24 :     ulong part_idx = fd_wksp_private_pinfo_idx( wksp->part_tail_cidx );
     189         102 :     while( !fd_wksp_private_pinfo_idx_is_null( part_idx ) ) {
     190             : 
     191             :       /* Load partition metadata */
     192             : 
     193          78 :       ulong gaddr_lo = pinfo[ part_idx ].gaddr_lo;
     194          78 :       ulong gaddr_hi = pinfo[ part_idx ].gaddr_hi;
     195          78 :       ulong tag      = pinfo[ part_idx ].tag;
     196             : 
     197             :       /* If this partition holds an allocation, deterministically assign
     198             :          it to a cgroup in an approximately load balanced way such that
     199             :          the assignments will be identical for the same set of
     200             :          allocations and cgroup_cnt. */
     201             : 
     202          78 :       if( tag ) { /* ~50/50 */
     203             : 
     204             :         /* Sample a handful of cgroups and pick the least loaded to
     205             :            approximate a greedy load balance method.  We consider the
     206             :            most recently assigned cgroup (which was thought to be
     207             :            lightly loaded at the previous assignment), a cyclically
     208             :            sampled cgroup (ala striping) and two pseudo-randomly sampled
     209             :            cgroups based on the common hash of gaddr_lo (ala random
     210             :            assignment).  We don't care if our samples collide; we are
     211             :            just trying to improve on load balance over straight striping
     212             :            and random sampling (both of which are already asymptotically
     213             :            are load balanced as per the above).
     214             : 
     215             :            We could use a min-heap here but that would be
     216             :            algorithmically more expensive, more complex to implement and
     217             :            unlikely to improve load balance much futher (it would be
     218             :            the greedy load balance method, which is also asymptotically
     219             :            optimal but not perfect ... perfect load balance is a
     220             :            computationally hard knapsack like problem but pretty good
     221             :            load balance is easy). */
     222             : 
     223          42 :         {
     224          42 :           ulong h = fd_ulong_hash( gaddr_lo );
     225             : 
     226          42 :           ulong i0 = cgroup_idx;             ulong l0 = cgroup_load[ i0 ];
     227          42 :           ulong i1 = cgroup_cursor;          ulong l1 = cgroup_load[ i1 ];
     228          42 :           ulong i2 =  h        % cgroup_cnt; ulong l2 = cgroup_load[ i2 ];
     229          42 :           ulong i3 = (h >> 32) % cgroup_cnt; ulong l3 = cgroup_load[ i3 ];
     230             : 
     231          42 :           i0 = fd_ulong_if( l0<=l1, i0, i1 ); l0 = fd_ulong_min( l0, l1 );
     232          42 :           i1 = fd_ulong_if( l2<=l3, i2, i3 ); l1 = fd_ulong_min( l2, l3 );
     233          42 :           i0 = fd_ulong_if( l0<=l1, i0, i1 ); l0 = fd_ulong_min( l0, l1 );
     234             : 
     235          42 :           cgroup_cursor = fd_ulong_if( cgroup_cursor<cgroup_cnt-1UL, cgroup_cursor+1UL, 0UL );
     236          42 :           cgroup_idx    = i0;
     237          42 :         }
     238             : 
     239             :         /* Update this cgroup's partition count and load.  The load is
     240             :            currently the total uncompressed bytes of partition metadata
     241             :            and data (TODO: consider adding a fixed base cost here to
     242             :            account for fixed computational overheads too.  This would be
     243             :            an order of magnitude ballpark of the cost of doing 2
     244             :            fd_checkpt_buf relative to the marginal cost of checkpointing
     245             :            an additional byte for some representative target ... note
     246             :            that specific target details should not be incorporated into
     247             :            this because then specific checkpt byte stream would be
     248             :            sensitive to who wrote the checkpt and ideally checkpt should
     249             :            be bit-for-bit identical for identical wksp regardless of the
     250             :            target details). */
     251             : 
     252          42 :         cgroup_alloc_cnt[ cgroup_idx ]++;
     253          42 :         cgroup_load     [ cgroup_idx ] += 3UL*sizeof(ulong) + (gaddr_hi - gaddr_lo);
     254             : 
     255             :         /* Push this partition onto the cgroup's stack.  Since we are
     256             :            iterating over partitions in reverse order by gaddr_lo, the
     257             :            stack for each cgroup can be treated as a linked list in
     258             :            sorted order by gaddr_lo (helps with metdata
     259             :            compressibility). */
     260             : 
     261          42 :         pinfo[ part_idx ].stack_cidx   = cgroup_head_cidx[ cgroup_idx ];
     262          42 :         cgroup_head_cidx[ cgroup_idx ] = fd_wksp_private_pinfo_cidx( part_idx );
     263          42 :       }
     264             : 
     265             :       /* Advance to the previous partition */
     266             : 
     267          78 :       part_idx = fd_wksp_private_pinfo_idx( pinfo[ part_idx ].prev_cidx );
     268          78 :     }
     269          24 :   }
     270             : 
     271             :   /* At this point, each wksp partitions to checkpt have been assigned
     272             :      to a cgroup, the cgroups are approximately load balanced and the
     273             :      partitions for each cgroup are given in a singly linked list sorted
     274             :      in ascending order by gaddr_lo. */
     275             : 
     276             :   /* Create the checkpt file */
     277             : 
     278          24 :   {
     279          24 :     mode_t old_mask = umask( (mode_t)0 );
     280          24 :     fd = open( path, O_CREAT|O_EXCL|O_WRONLY, (mode_t)mode );
     281          24 :     umask( old_mask );
     282          24 :     if( FD_UNLIKELY( fd==-1 ) ) {
     283           6 :       FD_LOG_WARNING(( "checkpt wksp \"%s\" to \"%s\" failed opening file with flags_O_CREAT|O_EXCL|O_WRONLY in mode 0%03lo "
     284           6 :                       "(%i-%s); attempting to continue", name, path, mode, errno, fd_io_strerror( errno ) ));
     285           6 :       err_fail = FD_WKSP_ERR_FAIL;
     286           6 :       goto fail;
     287           6 :     }
     288          24 :   }
     289             : 
     290             :   /* Initialize the checkpt */
     291             : 
     292          18 :   ulong frame_off[ FD_WKSP_CHECKPT_V2_CGROUP_MAX+6UL ];
     293          18 :   ulong frame_cnt = 0UL;
     294             : 
     295          18 :   fd_checkpt_t  _checkpt[ 1 ];
     296          18 :   uchar         wbuf[ FD_CHECKPT_WBUF_MIN ];
     297             : 
     298          18 :   checkpt = fd_checkpt_init_stream( _checkpt, fd, wbuf, FD_CHECKPT_WBUF_MIN ); /* logs details */
     299          18 :   if( FD_UNLIKELY( !checkpt ) ) {
     300           0 :     FD_LOG_WARNING(( "checkpt wksp \"%s\" to \"%s\" failed when initializing; attempting to continue", name, path ));
     301           0 :     err_fail = FD_WKSP_ERR_FAIL;
     302           0 :     goto fail;
     303           0 :   }
     304             : 
     305          99 : # define CHECKPT_OPEN(frame_style) do {                                                                                \
     306          99 :     int _err = fd_checkpt_open_advanced( checkpt, (frame_style), &frame_off[ frame_cnt ] );                            \
     307          99 :     if( FD_UNLIKELY( _err ) ) {                                                                                        \
     308           0 :       FD_LOG_WARNING(( "checkpt wksp \"%s\" to \"%s\" failed when opening a %s frame (%i-%s); attempting to continue", \
     309           0 :                        name, path, #frame_style, _err, fd_checkpt_strerror( _err ) ));                                 \
     310           0 :       err_fail = FD_WKSP_ERR_FAIL;                                                                                     \
     311           0 :       goto fail;                                                                                                       \
     312           0 :     }                                                                                                                  \
     313          99 :   } while(0)
     314             : 
     315          99 : # define CHECKPT_CLOSE() do {                                                                                       \
     316          99 :     frame_cnt++;                                                                                                    \
     317          99 :     int   _err = fd_checkpt_close_advanced( checkpt, &frame_off[ frame_cnt ] ); /* logs details */                  \
     318          99 :     if( FD_UNLIKELY( _err ) ) {                                                                                     \
     319           0 :       FD_LOG_WARNING(( "checkpt wksp \"%s\" to \"%s\" failed when closing a frame (%i-%s); attempting to continue", \
     320           0 :                        name, path, _err, fd_checkpt_strerror( _err ) ));                                            \
     321           0 :       err_fail = FD_WKSP_ERR_FAIL;                                                                                  \
     322           0 :       goto fail;                                                                                                    \
     323           0 :     }                                                                                                               \
     324          99 :   } while(0)
     325             : 
     326             :   /* Note: sz must be at most FD_CHECKPT_META_MAX */
     327          75 : # define CHECKPT_META( meta, sz ) do {                                                                                \
     328          75 :     ulong _sz  = (sz);                                                                                                \
     329          75 :     int   _err = fd_checkpt_meta( checkpt, (meta), _sz ); /* logs details */                                          \
     330          75 :     if( FD_UNLIKELY( _err ) ) {                                                                                       \
     331           0 :       FD_LOG_WARNING(( "checkpt to \"%s\" failed when writing %lu bytes metadata %s (%i-%s); attempting to continue", \
     332           0 :                        path, _sz, #meta, _err, fd_checkpt_strerror( _err ) ));                                        \
     333           0 :       err_fail = FD_WKSP_ERR_FAIL;                                                                                    \
     334           0 :       goto fail;                                                                                                      \
     335           0 :     }                                                                                                                 \
     336          75 :   } while(0)
     337             : 
     338             :   /* Note: data must exist and be unchanged until frame close */
     339         138 : # define CHECKPT_DATA( data, sz ) do {                                                                            \
     340         138 :     ulong _sz  = (sz);                                                                                            \
     341         138 :     int   _err = fd_checkpt_data( checkpt, (data), _sz ); /* logs details */                                      \
     342         138 :     if( FD_UNLIKELY( _err ) ) {                                                                                   \
     343           0 :       FD_LOG_WARNING(( "checkpt to \"%s\" failed when writing %lu bytes data %s (%i-%s); attempting to continue", \
     344           0 :                        path, _sz, #data, _err, fd_checkpt_strerror( _err ) ));                                    \
     345           0 :       err_fail = FD_WKSP_ERR_FAIL;                                                                                \
     346           0 :       goto fail;                                                                                                  \
     347           0 :     }                                                                                                             \
     348         138 :   } while(0)
     349             : 
     350             :   /* Checkpt the header */
     351             : 
     352          18 :   {
     353          18 :     fd_wksp_checkpt_v2_hdr_t hdr[1];
     354             : 
     355          18 :     hdr->magic                  = wksp->magic;
     356          18 :     hdr->style                  = FD_WKSP_CHECKPT_STYLE_V2;
     357          18 :     hdr->frame_style_compressed = frame_style_compressed;
     358          18 :     hdr->reserved               = 0U;
     359          18 :     memset( hdr->name, 0,    FD_SHMEM_NAME_MAX ); /* Make sure trailing zeros clear */
     360          18 :     memcpy( hdr->name, name, name_len          );
     361          18 :     hdr->seed                   = wksp->seed;
     362          18 :     hdr->part_max               = wksp->part_max;
     363          18 :     hdr->data_max               = wksp->data_max;
     364             : 
     365          18 :     CHECKPT_OPEN( FD_CHECKPT_FRAME_STYLE_RAW );
     366          18 :     CHECKPT_DATA( hdr, sizeof(fd_wksp_checkpt_v2_hdr_t) );
     367          18 :     CHECKPT_CLOSE();
     368          18 :   }
     369             : 
     370             :   /* Checkpt the info */
     371             : 
     372          18 :   {
     373          18 :     fd_wksp_checkpt_v2_info_t info[1];
     374          18 :     char                      buf[ 65536 ];
     375          18 :     char *                    p = buf;
     376             : 
     377          18 :     info->mode      = mode;
     378          18 :     info->wallclock = fd_log_wallclock();
     379          18 :     info->app_id    = fd_log_app_id   ();
     380          18 :     info->thread_id = fd_log_thread_id();
     381          18 :     info->host_id   = fd_log_host_id  ();
     382          18 :     info->cpu_id    = fd_log_cpu_id   ();
     383          18 :     info->group_id  = fd_log_group_id ();
     384          18 :     info->tid       = fd_log_tid      ();
     385          18 :     info->user_id   = fd_log_user_id  ();
     386             : 
     387         162 : #   define APPEND_CSTR( field, cstr, len ) do { \
     388         162 :       ulong _len = (len);                       \
     389         162 :       memcpy( p, (cstr), _len );                \
     390         162 :       p[ _len ] = '\0';                         \
     391         162 :       info->sz_##field = _len + 1UL;            \
     392         162 :       p += info->sz_##field;                    \
     393         162 :     } while(0)
     394             : 
     395          18 :     APPEND_CSTR( app,    fd_log_app(),    strlen( fd_log_app()    ) ); /* appends at most FD_LOG_NAME_MAX ~ 40 B */
     396          18 :     APPEND_CSTR( thread, fd_log_thread(), strlen( fd_log_thread() ) ); /* " */
     397          18 :     APPEND_CSTR( host,   fd_log_host(),   strlen( fd_log_host()   ) ); /* " */
     398          18 :     APPEND_CSTR( cpu,    fd_log_cpu(),    strlen( fd_log_cpu()    ) ); /* " */
     399          18 :     APPEND_CSTR( group,  fd_log_group(),  strlen( fd_log_group()  ) ); /* " */
     400          18 :     APPEND_CSTR( user,   fd_log_user(),   strlen( fd_log_user()   ) ); /* " */
     401          18 :     APPEND_CSTR( path,   path,            strlen( path            ) ); /* appends at most PATH_MAX-1 ~ 4 KiB */
     402          18 :     APPEND_CSTR( binfo,  binfo,           fd_cstr_nlen( binfo, FD_WKSP_CHECKPT_V2_BINFO_MAX-1UL ) ); /* appends at most 16 KiB */
     403          18 :     APPEND_CSTR( uinfo,  uinfo,           fd_cstr_nlen( uinfo, FD_WKSP_CHECKPT_V2_UINFO_MAX-1UL ) ); /* " */
     404             : 
     405          18 : #   undef APPEND_CSTR
     406             : 
     407             :     /* Write the info */
     408             : 
     409          18 :     CHECKPT_OPEN( frame_style_compressed );
     410          18 :     CHECKPT_DATA( info, sizeof(fd_wksp_checkpt_v2_info_t) );
     411          18 :     CHECKPT_DATA( buf,  (ulong)(p-buf)                    );
     412          18 :     CHECKPT_CLOSE();
     413          18 :   }
     414             : 
     415             :   /* Checkpt the volume cgroups.  Note: This implementation just
     416             :      checkpoints 1 volume with at most CGROUP_MAX cgroup_cnt groups.
     417             : 
     418             :      Note: this loop can be parallelized over multiple threads if
     419             :      willing to leave holes in the file (and then maybe do a second pass
     420             :      to compact the holes or maybe do a planning pass and then a real
     421             :      pass or maybe leave the holes and do a second pass of run length
     422             :      and entropy coding or maybe write to separate files and distribute
     423             :      as a multiple files or maybe use non-POSIX filesystem mojo to
     424             :      stitch together the separate files to appear as one file or ...) */
     425             : 
     426          27 :   for( ulong cgroup_idx=0UL; cgroup_idx<cgroup_cnt; cgroup_idx++ ) {
     427             : 
     428           9 :     CHECKPT_OPEN( frame_style_compressed );
     429             : 
     430             :     /* Write cgroup commands */
     431             : 
     432           9 :     fd_wksp_checkpt_v2_cmd_t cmd[1];
     433             : 
     434           9 :     ulong part_idx = fd_wksp_private_pinfo_idx( cgroup_head_cidx[ cgroup_idx ] );
     435          39 :     while( !fd_wksp_private_pinfo_idx_is_null( part_idx ) ) {
     436             : 
     437             :       /* Command: "meta (tag,gaddr_lo,gaddr_hi)" */
     438             : 
     439          30 :       cmd->meta.tag      = pinfo[ part_idx ].tag;      /* Note: non-zero */
     440          30 :       cmd->meta.gaddr_lo = pinfo[ part_idx ].gaddr_lo;
     441          30 :       cmd->meta.gaddr_hi = pinfo[ part_idx ].gaddr_hi;
     442             : 
     443          30 :       CHECKPT_META( cmd, sizeof(fd_wksp_checkpt_v2_cmd_t) );
     444             : 
     445          30 :       part_idx = fd_wksp_private_pinfo_idx( pinfo[ part_idx ].stack_cidx );
     446          30 :     }
     447             : 
     448             :     /* Command: "corresponding data follows" */
     449             : 
     450           9 :     cmd->data.tag        = 0UL;
     451           9 :     cmd->data.cgroup_cnt = ULONG_MAX;
     452           9 :     cmd->data.frame_off  = ULONG_MAX;
     453             : 
     454           9 :     CHECKPT_META( cmd, sizeof(fd_wksp_checkpt_v2_cmd_t) );
     455             : 
     456             :     /* Write cgroup partition data */
     457             : 
     458           9 :     part_idx = fd_wksp_private_pinfo_idx( cgroup_head_cidx[ cgroup_idx ] );
     459          39 :     while( !fd_wksp_private_pinfo_idx_is_null( part_idx ) ) {
     460          30 :       ulong gaddr_lo = pinfo[ part_idx ].gaddr_lo;
     461          30 :       ulong gaddr_hi = pinfo[ part_idx ].gaddr_hi;
     462             : 
     463             : #     if FD_HAS_DEEPASAN
     464             :       /* We read the whole partition, so unpoison the holes sub-allocators
     465             :          leave poisoned (permanently discarding that finer poisoning). */
     466             :       fd_asan_unpoison( fd_wksp_laddr_fast( wksp, gaddr_lo ), gaddr_hi - gaddr_lo );
     467             : #     endif
     468             : 
     469          30 :       CHECKPT_DATA( fd_wksp_laddr_fast( wksp, gaddr_lo ), gaddr_hi - gaddr_lo );
     470             : 
     471          30 :       part_idx = fd_wksp_private_pinfo_idx( pinfo[ part_idx ].stack_cidx );
     472          30 :     }
     473             : 
     474           9 :     CHECKPT_CLOSE();
     475             : 
     476           9 :   }
     477             : 
     478             :   /* Checkpt the volume appendix.  This starts with a command that
     479             :      indicates this frame is an appendix for cgroup_cnt cgroups (this
     480             :      can be used in a streaming restore to tell when it has reached the
     481             :      appendix and in a parallel restore of the appendix so a parallel
     482             :      restore thread knows how much it needs to decompress), the offsets
     483             :      of each cgroup frame (so parallel restore threads can seek to the
     484             :      partitions assigned to them) and the number of partitions in each
     485             :      cgroup frame (so that the pinfo on restore can be partitioned over
     486             :      parallel restore threads upfront non-atomically and
     487             :      deterministically). */
     488             : 
     489          18 :   {
     490          18 :     fd_wksp_checkpt_v2_cmd_t cmd[1];
     491             : 
     492             :     /* Command: "appendix for a volume with cgroup_cnt frames and no
     493             :        previous volumes" */
     494             : 
     495          18 :     cmd->appendix.tag        = 0UL;
     496          18 :     cmd->appendix.cgroup_cnt = cgroup_cnt;
     497          18 :     cmd->appendix.frame_off  = 0UL;
     498             : 
     499          18 :     CHECKPT_OPEN( frame_style_compressed );
     500          18 :     CHECKPT_META( cmd,              sizeof(fd_wksp_checkpt_v2_cmd_t) ); /* Note: must be meta for restore */
     501          18 :     CHECKPT_DATA( frame_off+2UL,    cgroup_cnt*sizeof(ulong)         );
     502          18 :     CHECKPT_DATA( cgroup_alloc_cnt, cgroup_cnt*sizeof(ulong)         );
     503          18 :     CHECKPT_CLOSE();
     504          18 :   }
     505             : 
     506             :   /* Checkpt the volumes frame */
     507             : 
     508          18 :   {
     509          18 :     fd_wksp_checkpt_v2_cmd_t cmd[1];
     510             : 
     511             :     /* Command: "no more volumes */
     512             : 
     513          18 :     cmd->volumes.tag        = 0UL;
     514          18 :     cmd->volumes.cgroup_cnt = ULONG_MAX;
     515          18 :     cmd->volumes.frame_off  = frame_off[ frame_cnt-1 ];
     516             : 
     517          18 :     CHECKPT_OPEN( frame_style_compressed );
     518          18 :     CHECKPT_META( cmd, sizeof(fd_wksp_checkpt_v2_cmd_t) );
     519          18 :     CHECKPT_CLOSE();
     520          18 :   }
     521             : 
     522             :   /* Checkpt the footer */
     523             : 
     524          18 :   {
     525          18 :     fd_wksp_checkpt_v2_ftr_t ftr[1];
     526             : 
     527             :     /* Command: "footer for a checkpt with cgroup_cnt total cgroup
     528             :        frames" */
     529             : 
     530          18 :     ftr->alloc_cnt                   = alloc_cnt;
     531          18 :     ftr->cgroup_cnt                  = cgroup_cnt;
     532          18 :     ftr->volume_cnt                  = 1UL;
     533          18 :     ftr->frame_off                   = frame_off[ frame_cnt-1U ];
     534          18 :     ftr->checkpt_sz                  = frame_off[ frame_cnt ] + sizeof(fd_wksp_checkpt_v2_ftr_t);
     535          18 :     ftr->data_max                    = wksp->data_max;
     536          18 :     ftr->part_max                    = wksp->part_max;
     537          18 :     ftr->seed                        = wksp->seed;
     538          18 :     memset( ftr->name, 0,    FD_SHMEM_NAME_MAX ); /* Make sure trailing zeros clear */
     539          18 :     memcpy( ftr->name, name, name_len          );
     540          18 :     ftr->reserved                    = 0U;
     541          18 :     ftr->frame_style_compressed      = frame_style_compressed;
     542          18 :     ftr->style                       = FD_WKSP_CHECKPT_STYLE_V2;
     543          18 :     ftr->unmagic                     = ~wksp->magic;
     544             : 
     545          18 :     CHECKPT_OPEN( FD_CHECKPT_FRAME_STYLE_RAW );
     546          18 :     CHECKPT_DATA( ftr, sizeof(fd_wksp_checkpt_v2_ftr_t) );
     547          18 :     CHECKPT_CLOSE();
     548          18 :   }
     549             : 
     550          18 : # undef CHECKPT_DATA
     551          18 : # undef CHECKPT_META
     552          18 : # undef CHECKPT_CLOSE
     553          18 : # undef CHECKPT_OPEN
     554             : 
     555             :   /* Finalize the checkpt */
     556             : 
     557          18 :   if( FD_UNLIKELY( !fd_checkpt_fini( checkpt ) ) ) { /* logs details */
     558           0 :     FD_LOG_WARNING(( "checkpt wksp \"%s\" to \"%s\" failed when finalizing; attempting to continue", name, path ));
     559           0 :     checkpt  = NULL;
     560           0 :     err_fail = FD_WKSP_ERR_FAIL;
     561           0 :     goto fail;
     562           0 :   }
     563             : 
     564             :   /* Close the file */
     565             : 
     566          18 :   if( FD_UNLIKELY( close( fd ) ) ) {
     567           0 :     FD_LOG_WARNING(( "checkpt wksp \"%s\" to \"%s\" failed when closing; attempting to continue", name, path ));
     568           0 :     fd       = -1;
     569           0 :     err_fail = FD_WKSP_ERR_FAIL;
     570           0 :     goto fail;
     571           0 :   }
     572             : 
     573             :   /* Unlock the wksp */
     574             : 
     575          18 :   fd_wksp_private_unlock( wksp );
     576          18 :   locked = 0;
     577             : 
     578          18 :   return FD_WKSP_SUCCESS;
     579             : 
     580           6 : fail:
     581             : 
     582             :   /* Release resources that might be reserved */
     583             : 
     584           6 :   if( FD_LIKELY( checkpt ) ) {
     585           0 :     if( FD_UNLIKELY( fd_checkpt_in_frame( checkpt ) ) && FD_UNLIKELY( fd_checkpt_close( checkpt ) ) )
     586           0 :       FD_LOG_WARNING(( "fd_checkpt_close failed; attempting to continue" ));
     587             : 
     588           0 :     if( FD_UNLIKELY( !fd_checkpt_fini( checkpt ) ) ) /* logs details */
     589           0 :       FD_LOG_WARNING(( "fd_checkpt_fini failed; attempting to continue" ));
     590           0 :   }
     591             : 
     592           6 :   if( FD_LIKELY( fd!=-1 ) && FD_UNLIKELY( close( fd ) ) )
     593           0 :     FD_LOG_WARNING(( "close(\"%s\") failed (%i-%s); attempting to continue", path, errno, fd_io_strerror( errno ) ));
     594             : 
     595           6 :   if( FD_LIKELY( locked ) ) fd_wksp_private_unlock( wksp );
     596             : 
     597           6 :   return err_fail;
     598          18 : }

Generated by: LCOV version 1.14