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