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