LCOV - code coverage report
Current view: top level - util/wksp - fd_wksp_checkpt_v2.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 306 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 1 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 <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           0 : #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           0 :                             int          frame_style_compressed ) {
      24             : 
      25           0 :   (void)tpool; (void)t0; (void)t1; /* Thread parallelization not currently implemented */
      26             : 
      27           0 :   char const * binfo = fd_log_build_info;
      28             : 
      29           0 :   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           0 :   int err_fail;
      35             : 
      36           0 :   int            locked  =  0;
      37           0 :   int            fd      = -1;
      38           0 :   fd_checkpt_t * checkpt = NULL;
      39             : 
      40           0 :   fd_wksp_private_pinfo_t * pinfo = fd_wksp_private_pinfo( wksp );
      41             : 
      42           0 :   char const * name     = wksp->name;
      43           0 :   ulong        name_len = fd_shmem_name_len( name );
      44           0 :   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           0 :   {
      53           0 :     int _err = fd_wksp_private_lock( wksp ); /* logs details */
      54           0 :     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           0 :     locked = 1;
      60           0 :   }
      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           0 :   ulong alloc_cnt = 0UL;
      68             : 
      69           0 :   {
      70           0 : #   define WKSP_TEST( c ) do {                                                                                  \
      71           0 :       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           0 :     } 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           0 :     ulong part_max  = wksp->part_max;
      84           0 :     ulong data_lo   = wksp->gaddr_lo;
      85           0 :     ulong data_hi   = wksp->gaddr_hi;
      86           0 :     ulong cycle_tag = wksp->cycle_tag++;
      87             : 
      88           0 :     WKSP_TEST( (0UL<data_lo) & (data_lo<=data_hi) ); /* Valid data region */
      89             : 
      90           0 :     ulong gaddr_last = data_hi;
      91             : 
      92           0 :     ulong part_idx = fd_wksp_private_pinfo_idx( wksp->part_tail_cidx );
      93           0 :     while( !fd_wksp_private_pinfo_idx_is_null( part_idx ) ) {
      94             : 
      95             :       /* Load partition metadata and validate it */
      96             : 
      97           0 :       WKSP_TEST( part_idx<part_max );                      /* Valid idx */
      98           0 :       WKSP_TEST( pinfo[ part_idx ].cycle_tag!=cycle_tag ); /* No cycles */
      99           0 :       pinfo[ part_idx ].cycle_tag = cycle_tag;             /* Mark part_idx as visited */
     100             : 
     101           0 :       ulong gaddr_lo = pinfo[ part_idx ].gaddr_lo;
     102           0 :       ulong gaddr_hi = pinfo[ part_idx ].gaddr_hi;
     103           0 :       ulong tag      = pinfo[ part_idx ].tag;
     104             : 
     105           0 :       WKSP_TEST( (data_lo<=gaddr_lo) & (gaddr_lo<gaddr_hi) & (gaddr_hi==gaddr_last) ); /* Valid partition range */
     106           0 :       gaddr_last = gaddr_lo;
     107             : 
     108             :       /* If this partition holds an allocation, count it */
     109             : 
     110           0 :       alloc_cnt += (ulong)(tag>0UL);
     111             : 
     112             :       /* Advance to the previous partition */
     113             : 
     114           0 :       part_idx = fd_wksp_private_pinfo_idx( pinfo[ part_idx ].prev_cidx );
     115           0 :     }
     116             : 
     117           0 :     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           0 :     cgroup_cnt = fd_ulong_min( (alloc_cnt+31UL)/32UL, FD_WKSP_CHECKPT_V2_CGROUP_MAX );
     157             : 
     158           0 : #   undef WKSP_TEST
     159           0 :   }
     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           0 :   ulong cgroup_alloc_cnt[ FD_WKSP_CHECKPT_V2_CGROUP_MAX ]; /* Number of partitions in each cgroup */
     167             : 
     168           0 :   {
     169             : 
     170             :     /* Initialize the cgroups to empty */
     171             : 
     172           0 :     ulong cgroup_load[ FD_WKSP_CHECKPT_V2_CGROUP_MAX ];
     173             : 
     174           0 :     uint null_cidx = fd_wksp_private_pinfo_cidx( FD_WKSP_PRIVATE_PINFO_IDX_NULL );
     175           0 :     for( ulong cgroup_idx=0UL; cgroup_idx<cgroup_cnt; cgroup_idx++ ) {
     176           0 :       cgroup_head_cidx[ cgroup_idx ] = null_cidx;
     177           0 :       cgroup_alloc_cnt[ cgroup_idx ] = 0UL;
     178           0 :       cgroup_load     [ cgroup_idx ] = 0UL;
     179           0 :     }
     180             : 
     181             :     /* Configure cgroup sampling */
     182             : 
     183           0 :     ulong cgroup_cursor = 0UL;
     184           0 :     ulong cgroup_idx    = 0UL;
     185             : 
     186             :     /* For all partitions in reverse order by gaddr_lo */
     187             : 
     188           0 :     ulong part_idx = fd_wksp_private_pinfo_idx( wksp->part_tail_cidx );
     189           0 :     while( !fd_wksp_private_pinfo_idx_is_null( part_idx ) ) {
     190             : 
     191             :       /* Load partition metadata */
     192             : 
     193           0 :       ulong gaddr_lo = pinfo[ part_idx ].gaddr_lo;
     194           0 :       ulong gaddr_hi = pinfo[ part_idx ].gaddr_hi;
     195           0 :       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           0 :       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           0 :         {
     224           0 :           ulong h = fd_ulong_hash( gaddr_lo );
     225             : 
     226           0 :           ulong i0 = cgroup_idx;             ulong l0 = cgroup_load[ i0 ];
     227           0 :           ulong i1 = cgroup_cursor;          ulong l1 = cgroup_load[ i1 ];
     228           0 :           ulong i2 =  h        % cgroup_cnt; ulong l2 = cgroup_load[ i2 ];
     229           0 :           ulong i3 = (h >> 32) % cgroup_cnt; ulong l3 = cgroup_load[ i3 ];
     230             : 
     231           0 :           i0 = fd_ulong_if( l0<=l1, i0, i1 ); l0 = fd_ulong_min( l0, l1 );
     232           0 :           i1 = fd_ulong_if( l2<=l3, i2, i3 ); l1 = fd_ulong_min( l2, l3 );
     233           0 :           i0 = fd_ulong_if( l0<=l1, i0, i1 ); l0 = fd_ulong_min( l0, l1 );
     234             : 
     235           0 :           cgroup_cursor = fd_ulong_if( cgroup_cursor<cgroup_cnt-1UL, cgroup_cursor+1UL, 0UL );
     236           0 :           cgroup_idx    = i0;
     237           0 :         }
     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           0 :         cgroup_alloc_cnt[ cgroup_idx ]++;
     253           0 :         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           0 :         pinfo[ part_idx ].stack_cidx   = cgroup_head_cidx[ cgroup_idx ];
     262           0 :         cgroup_head_cidx[ cgroup_idx ] = fd_wksp_private_pinfo_cidx( part_idx );
     263           0 :       }
     264             : 
     265             :       /* Advance to the previous partition */
     266             : 
     267           0 :       part_idx = fd_wksp_private_pinfo_idx( pinfo[ part_idx ].prev_cidx );
     268           0 :     }
     269           0 :   }
     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           0 :   {
     279           0 :     mode_t old_mask = umask( (mode_t)0 );
     280           0 :     fd = open( path, O_CREAT|O_EXCL|O_WRONLY, (mode_t)mode );
     281           0 :     umask( old_mask );
     282           0 :     if( FD_UNLIKELY( fd==-1 ) ) {
     283           0 :       FD_LOG_WARNING(( "checkpt wksp \"%s\" to \"%s\" failed opening file with flags_O_CREAT|O_EXCL|O_WRONLY in mode 0%03lo "
     284           0 :                       "(%i-%s); attempting to continue", name, path, mode, errno, fd_io_strerror( errno ) ));
     285           0 :       err_fail = FD_WKSP_ERR_FAIL;
     286           0 :       goto fail;
     287           0 :     }
     288           0 :   }
     289             : 
     290             :   /* Initialize the checkpt */
     291             : 
     292           0 :   ulong frame_off[ FD_WKSP_CHECKPT_V2_CGROUP_MAX+6UL ];
     293           0 :   ulong frame_cnt = 0UL;
     294             : 
     295           0 :   fd_checkpt_t  _checkpt[ 1 ];
     296           0 :   uchar         wbuf[ FD_CHECKPT_WBUF_MIN ];
     297             : 
     298           0 :   checkpt = fd_checkpt_init_stream( _checkpt, fd, wbuf, FD_CHECKPT_WBUF_MIN ); /* logs details */
     299           0 :   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           0 : # define CHECKPT_OPEN(frame_style) do {                                                                                \
     306           0 :     int _err = fd_checkpt_open_advanced( checkpt, (frame_style), &frame_off[ frame_cnt ] );                            \
     307           0 :     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           0 :   } while(0)
     314             : 
     315           0 : # define CHECKPT_CLOSE() do {                                                                                       \
     316           0 :     frame_cnt++;                                                                                                    \
     317           0 :     int   _err = fd_checkpt_close_advanced( checkpt, &frame_off[ frame_cnt ] ); /* logs details */                  \
     318           0 :     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           0 :   } while(0)
     325             : 
     326             :   /* Note: sz must be at most FD_CHECKPT_META_MAX */
     327           0 : # define CHECKPT_META( meta, sz ) do {                                                                                \
     328           0 :     ulong _sz  = (sz);                                                                                                \
     329           0 :     int   _err = fd_checkpt_meta( checkpt, (meta), _sz ); /* logs details */                                          \
     330           0 :     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           0 :   } while(0)
     337             : 
     338             :   /* Note: data must exist and be unchanged until frame close */
     339           0 : # define CHECKPT_DATA( data, sz ) do {                                                                            \
     340           0 :     ulong _sz  = (sz);                                                                                            \
     341           0 :     int   _err = fd_checkpt_data( checkpt, (data), _sz ); /* logs details */                                      \
     342           0 :     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           0 :   } while(0)
     349             : 
     350             :   /* Checkpt the header */
     351             : 
     352           0 :   {
     353           0 :     fd_wksp_checkpt_v2_hdr_t hdr[1];
     354             : 
     355           0 :     hdr->magic                  = wksp->magic;
     356           0 :     hdr->style                  = FD_WKSP_CHECKPT_STYLE_V2;
     357           0 :     hdr->frame_style_compressed = frame_style_compressed;
     358           0 :     hdr->reserved               = 0U;
     359           0 :     memset( hdr->name, 0,    FD_SHMEM_NAME_MAX ); /* Make sure trailing zeros clear */
     360           0 :     memcpy( hdr->name, name, name_len          );
     361           0 :     hdr->seed                   = wksp->seed;
     362           0 :     hdr->part_max               = wksp->part_max;
     363           0 :     hdr->data_max               = wksp->data_max;
     364             : 
     365           0 :     CHECKPT_OPEN( FD_CHECKPT_FRAME_STYLE_RAW );
     366           0 :     CHECKPT_DATA( hdr, sizeof(fd_wksp_checkpt_v2_hdr_t) );
     367           0 :     CHECKPT_CLOSE();
     368           0 :   }
     369             : 
     370             :   /* Checkpt the info */
     371             : 
     372           0 :   {
     373           0 :     fd_wksp_checkpt_v2_info_t info[1];
     374           0 :     char                      buf[ 65536 ];
     375           0 :     char *                    p = buf;
     376             : 
     377           0 :     info->mode      = mode;
     378           0 :     info->wallclock = fd_log_wallclock();
     379           0 :     info->app_id    = fd_log_app_id   ();
     380           0 :     info->thread_id = fd_log_thread_id();
     381           0 :     info->host_id   = fd_log_host_id  ();
     382           0 :     info->cpu_id    = fd_log_cpu_id   ();
     383           0 :     info->group_id  = fd_log_group_id ();
     384           0 :     info->tid       = fd_log_tid      ();
     385           0 :     info->user_id   = fd_log_user_id  ();
     386             : 
     387           0 : #   define APPEND_CSTR( field, cstr, len ) do { \
     388           0 :       ulong _len = (len);                       \
     389           0 :       memcpy( p, (cstr), _len );                \
     390           0 :       p[ _len ] = '\0';                         \
     391           0 :       info->sz_##field = _len + 1UL;            \
     392           0 :       p += info->sz_##field;                    \
     393           0 :     } while(0)
     394             : 
     395           0 :     APPEND_CSTR( app,    fd_log_app(),    strlen( fd_log_app()    ) ); /* appends at most FD_LOG_NAME_MAX ~ 40 B */
     396           0 :     APPEND_CSTR( thread, fd_log_thread(), strlen( fd_log_thread() ) ); /* " */
     397           0 :     APPEND_CSTR( host,   fd_log_host(),   strlen( fd_log_host()   ) ); /* " */
     398           0 :     APPEND_CSTR( cpu,    fd_log_cpu(),    strlen( fd_log_cpu()    ) ); /* " */
     399           0 :     APPEND_CSTR( group,  fd_log_group(),  strlen( fd_log_group()  ) ); /* " */
     400           0 :     APPEND_CSTR( user,   fd_log_user(),   strlen( fd_log_user()   ) ); /* " */
     401           0 :     APPEND_CSTR( path,   path,            strlen( path            ) ); /* appends at most PATH_MAX-1 ~ 4 KiB */
     402           0 :     APPEND_CSTR( binfo,  binfo,           fd_cstr_nlen( binfo, FD_WKSP_CHECKPT_V2_BINFO_MAX-1UL ) ); /* appends at most 16 KiB */
     403           0 :     APPEND_CSTR( uinfo,  uinfo,           fd_cstr_nlen( uinfo, FD_WKSP_CHECKPT_V2_UINFO_MAX-1UL ) ); /* " */
     404             : 
     405           0 : #   undef APPEND_CSTR
     406             : 
     407             :     /* Write the info */
     408             : 
     409           0 :     CHECKPT_OPEN( frame_style_compressed );
     410           0 :     CHECKPT_DATA( info, sizeof(fd_wksp_checkpt_v2_info_t) );
     411           0 :     CHECKPT_DATA( buf,  (ulong)(p-buf)                    );
     412           0 :     CHECKPT_CLOSE();
     413           0 :   }
     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           0 :   for( ulong cgroup_idx=0UL; cgroup_idx<cgroup_cnt; cgroup_idx++ ) {
     427             : 
     428           0 :     CHECKPT_OPEN( frame_style_compressed );
     429             : 
     430             :     /* Write cgroup commands */
     431             : 
     432           0 :     fd_wksp_checkpt_v2_cmd_t cmd[1];
     433             : 
     434           0 :     ulong part_idx = fd_wksp_private_pinfo_idx( cgroup_head_cidx[ cgroup_idx ] );
     435           0 :     while( !fd_wksp_private_pinfo_idx_is_null( part_idx ) ) {
     436             : 
     437             :       /* Command: "meta (tag,gaddr_lo,gaddr_hi)" */
     438             : 
     439           0 :       cmd->meta.tag      = pinfo[ part_idx ].tag;      /* Note: non-zero */
     440           0 :       cmd->meta.gaddr_lo = pinfo[ part_idx ].gaddr_lo;
     441           0 :       cmd->meta.gaddr_hi = pinfo[ part_idx ].gaddr_hi;
     442             : 
     443           0 :       CHECKPT_META( cmd, sizeof(fd_wksp_checkpt_v2_cmd_t) );
     444             : 
     445           0 :       part_idx = fd_wksp_private_pinfo_idx( pinfo[ part_idx ].stack_cidx );
     446           0 :     }
     447             : 
     448             :     /* Command: "corresponding data follows" */
     449             : 
     450           0 :     cmd->data.tag        = 0UL;
     451           0 :     cmd->data.cgroup_cnt = ULONG_MAX;
     452           0 :     cmd->data.frame_off  = ULONG_MAX;
     453             : 
     454           0 :     CHECKPT_META( cmd, sizeof(fd_wksp_checkpt_v2_cmd_t) );
     455             : 
     456             :     /* Write cgroup partition data */
     457             : 
     458           0 :     part_idx = fd_wksp_private_pinfo_idx( cgroup_head_cidx[ cgroup_idx ] );
     459           0 :     while( !fd_wksp_private_pinfo_idx_is_null( part_idx ) ) {
     460           0 :       ulong gaddr_lo = pinfo[ part_idx ].gaddr_lo;
     461           0 :       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           0 :       CHECKPT_DATA( fd_wksp_laddr_fast( wksp, gaddr_lo ), gaddr_hi - gaddr_lo );
     470             : 
     471           0 :       part_idx = fd_wksp_private_pinfo_idx( pinfo[ part_idx ].stack_cidx );
     472           0 :     }
     473             : 
     474           0 :     CHECKPT_CLOSE();
     475             : 
     476           0 :   }
     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           0 :   {
     490           0 :     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           0 :     cmd->appendix.tag        = 0UL;
     496           0 :     cmd->appendix.cgroup_cnt = cgroup_cnt;
     497           0 :     cmd->appendix.frame_off  = 0UL;
     498             : 
     499           0 :     CHECKPT_OPEN( frame_style_compressed );
     500           0 :     CHECKPT_META( cmd,              sizeof(fd_wksp_checkpt_v2_cmd_t) ); /* Note: must be meta for restore */
     501           0 :     CHECKPT_DATA( frame_off+2UL,    cgroup_cnt*sizeof(ulong)         );
     502           0 :     CHECKPT_DATA( cgroup_alloc_cnt, cgroup_cnt*sizeof(ulong)         );
     503           0 :     CHECKPT_CLOSE();
     504           0 :   }
     505             : 
     506             :   /* Checkpt the volumes frame */
     507             : 
     508           0 :   {
     509           0 :     fd_wksp_checkpt_v2_cmd_t cmd[1];
     510             : 
     511             :     /* Command: "no more volumes */
     512             : 
     513           0 :     cmd->volumes.tag        = 0UL;
     514           0 :     cmd->volumes.cgroup_cnt = ULONG_MAX;
     515           0 :     cmd->volumes.frame_off  = frame_off[ frame_cnt-1 ];
     516             : 
     517           0 :     CHECKPT_OPEN( frame_style_compressed );
     518           0 :     CHECKPT_META( cmd, sizeof(fd_wksp_checkpt_v2_cmd_t) );
     519           0 :     CHECKPT_CLOSE();
     520           0 :   }
     521             : 
     522             :   /* Checkpt the footer */
     523             : 
     524           0 :   {
     525           0 :     fd_wksp_checkpt_v2_ftr_t ftr[1];
     526             : 
     527             :     /* Command: "footer for a checkpt with cgroup_cnt total cgroup
     528             :        frames" */
     529             : 
     530           0 :     ftr->alloc_cnt                   = alloc_cnt;
     531           0 :     ftr->cgroup_cnt                  = cgroup_cnt;
     532           0 :     ftr->volume_cnt                  = 1UL;
     533           0 :     ftr->frame_off                   = frame_off[ frame_cnt-1U ];
     534           0 :     ftr->checkpt_sz                  = frame_off[ frame_cnt ] + sizeof(fd_wksp_checkpt_v2_ftr_t);
     535           0 :     ftr->data_max                    = wksp->data_max;
     536           0 :     ftr->part_max                    = wksp->part_max;
     537           0 :     ftr->seed                        = wksp->seed;
     538           0 :     memset( ftr->name, 0,    FD_SHMEM_NAME_MAX ); /* Make sure trailing zeros clear */
     539           0 :     memcpy( ftr->name, name, name_len          );
     540           0 :     ftr->reserved                    = 0U;
     541           0 :     ftr->frame_style_compressed      = frame_style_compressed;
     542           0 :     ftr->style                       = FD_WKSP_CHECKPT_STYLE_V2;
     543           0 :     ftr->unmagic                     = ~wksp->magic;
     544             : 
     545           0 :     CHECKPT_OPEN( FD_CHECKPT_FRAME_STYLE_RAW );
     546           0 :     CHECKPT_DATA( ftr, sizeof(fd_wksp_checkpt_v2_ftr_t) );
     547           0 :     CHECKPT_CLOSE();
     548           0 :   }
     549             : 
     550           0 : # undef CHECKPT_DATA
     551           0 : # undef CHECKPT_META
     552           0 : # undef CHECKPT_CLOSE
     553           0 : # undef CHECKPT_OPEN
     554             : 
     555             :   /* Finalize the checkpt */
     556             : 
     557           0 :   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           0 :   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           0 :   fd_wksp_private_unlock( wksp );
     576           0 :   locked = 0;
     577             : 
     578           0 :   return FD_WKSP_SUCCESS;
     579             : 
     580           0 : fail:
     581             : 
     582             :   /* Release resources that might be reserved */
     583             : 
     584           0 :   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           0 :   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           0 :   if( FD_LIKELY( locked ) ) fd_wksp_private_unlock( wksp );
     596             : 
     597           0 :   return err_fail;
     598           0 : }

Generated by: LCOV version 1.14