Line data Source code
1 : #include "fd_wksp_private.h"
2 :
3 : #include <errno.h>
4 : #include <unistd.h>
5 : #include <fcntl.h>
6 : #include <sys/stat.h>
7 :
8 : /* fd_wksp_private_checkpt_v1_write writes size sz buffer buf to the
9 : output stream checkpt. Assumes checkpt is valid and not in a
10 : prepare. Returns 0 on success and non-zero on failure (will be an
11 : errno compat error code). */
12 :
13 : static inline int
14 : fd_wksp_private_checkpt_v1_write( fd_io_buffered_ostream_t * checkpt,
15 : void const * buf,
16 9 : ulong sz ) {
17 9 : return fd_io_buffered_ostream_write( checkpt, buf, sz );
18 9 : }
19 :
20 : /* fd_wksp_private_checkpt_v1_prepare prepares to write at most max
21 : bytes to the output stream checkpt. Assumes checkpt is valid and not
22 : in a prepare and max is at most checkpt's wbuf_sz. Returns the
23 : location in the caller's address space for preparing the max bytes on
24 : success (*_err will be 0) and NULL on failure (*_err will be an errno
25 : compat error code). */
26 :
27 : static inline void *
28 : fd_wksp_private_checkpt_v1_prepare( fd_io_buffered_ostream_t * checkpt,
29 : ulong max,
30 27 : int * _err ) {
31 27 : if( FD_UNLIKELY( fd_io_buffered_ostream_peek_sz( checkpt )<max ) ) {
32 0 : int err = fd_io_buffered_ostream_flush( checkpt );
33 0 : if( FD_UNLIKELY( err ) ) {
34 0 : *_err = err;
35 0 : return NULL;
36 0 : }
37 : /* At this point, peek_sz==wbuf_sz and wbuf_sz>=max */
38 0 : }
39 : /* At this point, peek_sz>=max */
40 27 : *_err = 0;
41 27 : return fd_io_buffered_ostream_peek( checkpt );
42 27 : }
43 :
44 : /* fd_wksp_private_checkpt_v1_publish publishes prepared bytes
45 : [prepare,next) to checkpt. Assumes checkpt is in a prepare and the
46 : number of bytes to publish is at most the prepare's max. checkpt
47 : will not be in a prepare on return. */
48 :
49 : static inline void
50 : fd_wksp_private_checkpt_v1_publish( fd_io_buffered_ostream_t * checkpt,
51 27 : void * next ) {
52 27 : fd_io_buffered_ostream_seek( checkpt, (ulong)next - (ulong)fd_io_buffered_ostream_peek( checkpt ) );
53 27 : }
54 :
55 : /* fd_wksp_private_checkpt_v1_cancel cancels a prepare. Assumes checkpt
56 : is valid and in a prepare. checkpt will not be in a prepare on
57 : return. */
58 :
59 : //static inline void fd_wksp_private_checkpt_v1_cancel( fd_io_buffered_ostream_t * checkpt ) { (void)checkpt; }
60 :
61 : /* fd_wksp_private_checkpt_v1_ulong checkpoints the value v into a
62 : checkpt. p points to the location in a prepare where v should be
63 : encoded. Assumes this location has svw_enc_sz(v) available (at least
64 : 1 and at most 9). Returns the location of the first byte after the
65 : encoded value (will be prep+svw_enc_sz(val)). */
66 :
67 234 : static inline void * fd_wksp_private_checkpt_v1_ulong( void * prep, ulong val ) { return fd_ulong_svw_enc( (uchar *)prep, val ); }
68 :
69 : /* fd_wksp_private_checkpt_v1_buf checkpoints a variable length buffer buf
70 : of size sz into a checkpt. p points to the location in a prepare
71 : region where buf should be encoded. Assumes this location has
72 : svw_enc_sz(sz)+sz bytes available (at least 1+sz and at most 9+sz).
73 : Returns the location of the first byte after the encoded buffer (will
74 : be prep+svw_enc_sz(sz)+sz). Zero sz is fine (and NULL buf is fine if
75 : sz is zero). */
76 :
77 : static inline void *
78 : fd_wksp_private_checkpt_v1_buf( void * prep,
79 : void const * buf,
80 81 : ulong sz ) {
81 81 : prep = fd_wksp_private_checkpt_v1_ulong( (uchar *)prep, sz );
82 81 : if( FD_LIKELY( sz ) ) fd_memcpy( prep, buf, sz );
83 81 : return (uchar *)prep + sz;
84 81 : }
85 :
86 : int
87 : fd_wksp_private_checkpt_v1( fd_tpool_t * tpool,
88 : ulong t0,
89 : ulong t1,
90 : fd_wksp_t * wksp,
91 : char const * path,
92 : ulong mode,
93 12 : char const * uinfo ) {
94 12 : (void)tpool; (void)t0; (void)t1; /* Note: Thread parallel v1 checkpoint not supported */
95 :
96 12 : char const * binfo = fd_log_build_info;
97 :
98 : //FD_LOG_INFO(( "Checkpt wksp \"%s\" to \"%s\" (mode 0%03lo), uinfo \"%s\"", wksp->name, path, mode, uinfo ));
99 :
100 12 : mode_t old_mask = umask( (mode_t)0 );
101 12 : int fd = open( path, O_CREAT|O_EXCL|O_WRONLY, (mode_t)mode );
102 12 : umask( old_mask );
103 12 : if( FD_UNLIKELY( fd==-1 ) ) {
104 3 : FD_LOG_WARNING(( "open(\"%s\",O_CREAT|O_EXCL|O_WRONLY,0%03lo) failed (%i-%s)", path, mode, errno, fd_io_strerror( errno ) ));
105 3 : return FD_WKSP_ERR_FAIL;
106 3 : }
107 :
108 9 : # define WBUF_ALIGN ( 4096UL)
109 18 : # define WBUF_FOOTPRINT (65536UL)
110 :
111 9 : uchar wbuf[ WBUF_FOOTPRINT ] __attribute__((aligned(WBUF_ALIGN)));
112 9 : fd_io_buffered_ostream_t checkpt[ 1 ];
113 9 : fd_io_buffered_ostream_init( checkpt, fd, wbuf, WBUF_FOOTPRINT );
114 :
115 9 : int err;
116 9 : uchar * prep;
117 :
118 9 : err = fd_wksp_private_lock( wksp ); if( FD_UNLIKELY( err ) ) goto fini; /* logs details */
119 :
120 : /* Do basic wksp checks */
121 :
122 9 : ulong data_lo = wksp->gaddr_lo;
123 9 : ulong data_hi = wksp->gaddr_hi;
124 9 : if( FD_UNLIKELY( !((0UL<data_lo) & (data_lo<=data_hi)) ) ) goto corrupt_wksp;
125 :
126 : //FD_LOG_INFO(( "Checkpt header and metadata" ));
127 :
128 9 : ulong binfo_len = fd_cstr_nlen( binfo, FD_WKSP_CHECKPT_V1_BINFO_MAX-1UL );
129 9 : ulong uinfo_len = fd_cstr_nlen( uinfo, FD_WKSP_CHECKPT_V1_UINFO_MAX-1UL );
130 :
131 9 : prep = fd_wksp_private_checkpt_v1_prepare( checkpt, WBUF_FOOTPRINT, &err ); if( FD_UNLIKELY( !prep ) ) goto io_err;
132 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, wksp->magic );
133 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, (ulong)FD_WKSP_CHECKPT_STYLE_V1 );
134 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, (ulong)wksp->seed );
135 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, wksp->part_max );
136 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, wksp->data_max );
137 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, (ulong)fd_log_wallclock() );
138 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_app_id() );
139 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_thread_id() );
140 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_host_id() );
141 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_cpu_id() );
142 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_group_id() );
143 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_tid() );
144 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, fd_log_user_id() );
145 9 : prep = fd_wksp_private_checkpt_v1_buf ( prep, wksp->name, strlen( wksp->name ) );
146 9 : prep = fd_wksp_private_checkpt_v1_buf ( prep, fd_log_app(), strlen( fd_log_app() ) );
147 9 : prep = fd_wksp_private_checkpt_v1_buf ( prep, fd_log_thread(), strlen( fd_log_thread() ) );
148 9 : prep = fd_wksp_private_checkpt_v1_buf ( prep, fd_log_host(), strlen( fd_log_host() ) );
149 9 : prep = fd_wksp_private_checkpt_v1_buf ( prep, fd_log_cpu(), strlen( fd_log_cpu() ) );
150 9 : prep = fd_wksp_private_checkpt_v1_buf ( prep, fd_log_group(), strlen( fd_log_group() ) );
151 9 : prep = fd_wksp_private_checkpt_v1_buf ( prep, fd_log_user(), strlen( fd_log_user() ) );
152 9 : prep = fd_wksp_private_checkpt_v1_buf ( prep, binfo, binfo_len );
153 9 : prep = fd_wksp_private_checkpt_v1_buf ( prep, uinfo, uinfo_len );
154 9 : fd_wksp_private_checkpt_v1_publish( checkpt, prep );
155 :
156 : //FD_LOG_INFO(( "Checkpt allocations" ));
157 :
158 9 : ulong part_max = wksp->part_max;
159 9 : fd_wksp_private_pinfo_t * pinfo = fd_wksp_private_pinfo( wksp );
160 :
161 9 : ulong cycle_tag = wksp->cycle_tag++;
162 :
163 9 : ulong gaddr_last = data_lo;
164 :
165 9 : ulong i = fd_wksp_private_pinfo_idx( wksp->part_head_cidx );
166 30 : while( !fd_wksp_private_pinfo_idx_is_null( i ) ) {
167 21 : if( FD_UNLIKELY( i>=part_max ) || FD_UNLIKELY( pinfo[ i ].cycle_tag==cycle_tag ) ) goto corrupt_wksp;
168 21 : pinfo[ i ].cycle_tag = cycle_tag; /* mark i as visited */
169 :
170 : /* Do basic partition checks */
171 :
172 21 : ulong gaddr_lo = pinfo[ i ].gaddr_lo;
173 21 : ulong gaddr_hi = pinfo[ i ].gaddr_hi;
174 21 : ulong tag = pinfo[ i ].tag;
175 :
176 21 : if( FD_UNLIKELY( !((gaddr_last==gaddr_lo) & (gaddr_lo<gaddr_hi) & (gaddr_hi<=data_hi)) ) ) goto corrupt_wksp;
177 :
178 21 : gaddr_last = gaddr_hi;
179 :
180 : /* If an allocated partition, checkpt it */
181 :
182 21 : if( tag ) { /* ~50/50 */
183 :
184 9 : ulong sz = gaddr_hi - gaddr_lo;
185 9 : void * laddr_lo = fd_wksp_laddr_fast( wksp, gaddr_lo );
186 :
187 : /* Checkpt partition header */
188 :
189 9 : prep = fd_wksp_private_checkpt_v1_prepare( checkpt, 3UL*9UL, &err ); if( FD_UNLIKELY( !prep ) ) goto io_err;
190 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, tag );
191 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, gaddr_lo );
192 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, sz );
193 9 : fd_wksp_private_checkpt_v1_publish( checkpt, prep );
194 :
195 : /* Checkpt partition data */
196 :
197 9 : err = fd_wksp_private_checkpt_v1_write( checkpt, laddr_lo, sz ); if( FD_UNLIKELY( err ) ) goto io_err;
198 9 : }
199 :
200 : /* Advance to next partition */
201 :
202 21 : i = fd_wksp_private_pinfo_idx( pinfo[ i ].next_cidx );
203 21 : }
204 :
205 : //FD_LOG_INFO(( "Checkpt footer" ));
206 :
207 9 : prep = fd_wksp_private_checkpt_v1_prepare( checkpt, 1UL*9UL, &err ); if( FD_UNLIKELY( !prep ) ) goto io_err;
208 9 : prep = fd_wksp_private_checkpt_v1_ulong( prep, 0UL ); /* tags are never 0 above */
209 9 : fd_wksp_private_checkpt_v1_publish( checkpt, prep );
210 :
211 9 : err = fd_io_buffered_ostream_flush( checkpt ); if( FD_UNLIKELY( err ) ) goto io_err;
212 :
213 9 : fd_wksp_private_unlock( wksp );
214 :
215 : //FD_LOG_INFO(( "Checkpt successful" ));
216 :
217 : /* note: err == 0 at this point */
218 :
219 9 : fini: /* note: wksp unlocked at this point */
220 9 : fd_io_buffered_ostream_fini( checkpt );
221 9 : if( FD_UNLIKELY( err ) && FD_UNLIKELY( unlink( path ) ) )
222 0 : FD_LOG_WARNING(( "unlink(\"%s\") failed (%i-%s); attempting to continue", path, errno, fd_io_strerror( errno ) ));
223 9 : if( FD_UNLIKELY( close( fd ) ) )
224 0 : FD_LOG_WARNING(( "close(\"%s\") failed (%i-%s); attempting to continue", path, errno, fd_io_strerror( errno ) ));
225 9 : return err;
226 :
227 0 : io_err: /* Failed due to I/O error ... clean up and log (note: wksp locked at this point) */
228 0 : fd_wksp_private_unlock( wksp );
229 0 : FD_LOG_WARNING(( "Checkpt wksp \"%s\" to \"%s\" failed due to I/O error (%i-%s)",
230 0 : wksp->name, path, err, fd_io_strerror( err ) ));
231 0 : err = FD_WKSP_ERR_FAIL;
232 0 : goto fini;
233 :
234 0 : corrupt_wksp: /* Failed due to wksp corruption ... clean up and log (note: wksp locked at this point) */
235 0 : fd_wksp_private_unlock( wksp );
236 0 : FD_LOG_WARNING(( "Checkpt wksp \"%s\" to \"%s\" failed due to wksp corruption", wksp->name, path ));
237 0 : err = FD_WKSP_ERR_CORRUPT;
238 0 : goto fini;
239 9 : }
|