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