Line data Source code
1 : #include "fd_vinyl_io.h"
2 :
3 : struct fd_vinyl_io_mm_rd;
4 : typedef struct fd_vinyl_io_mm_rd fd_vinyl_io_mm_rd_t;
5 :
6 : struct fd_vinyl_io_mm_rd {
7 : ulong ctx; /* Must mirror fd_vinyl_io_rd_t */
8 : ulong seq; /* " */
9 : void * dst; /* " */
10 : ulong sz; /* " */
11 : fd_vinyl_io_mm_rd_t * next; /* Next element in mm rd queue */
12 : };
13 :
14 : struct fd_vinyl_io_mm {
15 : fd_vinyl_io_t base[1];
16 : uchar * dev; /* Memory mapped I/O memory region */
17 : ulong dev_sync; /* Offset to the bstream's sync block (BLOCK_SZ multiple) */
18 : ulong dev_base; /* Offset to first block (BLOCK_SZ multiple) */
19 : ulong dev_sz; /* Block store byte size (BLOCK_SZ multiple) */
20 : fd_vinyl_io_mm_rd_t * rd_head; /* Pointer to queue head */
21 : fd_vinyl_io_mm_rd_t ** rd_tail_next; /* Pointer to queue &tail->next or &rd_head if empty. */
22 : fd_vinyl_bstream_block_t sync[1];
23 : /* spad_max bytes follow */
24 : };
25 :
26 : typedef struct fd_vinyl_io_mm fd_vinyl_io_mm_t;
27 :
28 : static void
29 : fd_vinyl_io_mm_read_imm( fd_vinyl_io_t * io,
30 : ulong seq0,
31 : void * _dst,
32 1500474 : ulong sz ) {
33 1500474 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
34 :
35 : /* If this is a request to read nothing, succeed immediately. If
36 : this is a request to read outside the bstream's past, fail. */
37 :
38 1500474 : if( FD_UNLIKELY( !sz ) ) return;
39 :
40 1316775 : uchar * dst = (uchar *)_dst;
41 1316775 : ulong seq1 = seq0 + sz;
42 :
43 1316775 : ulong seq_past = mm->base->seq_past;
44 1316775 : ulong seq_present = mm->base->seq_present;
45 :
46 1316775 : int bad_seq = !fd_ulong_is_aligned( seq0, FD_VINYL_BSTREAM_BLOCK_SZ );
47 1316775 : int bad_dst = (!fd_ulong_is_aligned( (ulong)dst, FD_VINYL_BSTREAM_BLOCK_SZ )) | !dst;
48 1316775 : int bad_sz = !fd_ulong_is_aligned( sz, FD_VINYL_BSTREAM_BLOCK_SZ );
49 1316775 : int bad_past = !(fd_vinyl_seq_le( seq_past, seq0 ) & fd_vinyl_seq_lt( seq0, seq1 ) & fd_vinyl_seq_le( seq1, seq_present ));
50 :
51 1316775 : if( FD_UNLIKELY( bad_seq | bad_dst | bad_sz | bad_past ) )
52 0 : FD_LOG_CRIT(( "bstream read_imm [%016lx,%016lx)/%lu failed (past [%016lx,%016lx)/%lu, %s)",
53 1316775 : seq0, seq1, sz, seq_past, seq_present, seq_present-seq_past,
54 1316775 : bad_seq ? "misaligned seq" :
55 1316775 : bad_dst ? "misaligned or NULL dst" :
56 1316775 : bad_sz ? "misaligned sz" :
57 1316775 : "not in past" ));
58 :
59 : /* At this point, we have a valid read request. Map seq0 into the
60 : bstream store. Read the lesser of sz bytes or until the store end.
61 : If we hit the store end with more to go, wrap around and finish the
62 : read at the store start. */
63 :
64 1316775 : uchar * dev = mm->dev;
65 1316775 : ulong dev_base = mm->dev_base;
66 1316775 : ulong dev_sz = mm->dev_sz;
67 :
68 1316775 : ulong dev_off = seq0 % dev_sz;
69 :
70 1316775 : ulong rsz = fd_ulong_min( sz, dev_sz - dev_off );
71 1316775 : memcpy( dst, dev + dev_base + dev_off, rsz );
72 1316775 : sz -= rsz;
73 1316775 : if( FD_UNLIKELY( sz ) ) memcpy( dst + rsz, dev + dev_base, sz );
74 1316775 : }
75 :
76 : static void
77 : fd_vinyl_io_mm_read( fd_vinyl_io_t * io,
78 1873707 : fd_vinyl_io_rd_t * _rd ) {
79 1873707 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *) io; /* Note: io must be non-NULL to have even been called */
80 1873707 : fd_vinyl_io_mm_rd_t * rd = (fd_vinyl_io_mm_rd_t *)_rd;
81 :
82 1873707 : rd->next = NULL;
83 1873707 : *mm->rd_tail_next = rd;
84 1873707 : mm->rd_tail_next = &rd->next;
85 :
86 1873707 : ulong seq0 = rd->seq;
87 1873707 : uchar * dst = (uchar *)rd->dst;
88 1873707 : ulong sz = rd->sz;
89 :
90 : /* If this is a request to read nothing, succeed immediately. If
91 : this is a request to read outside the bstream's past, fail. */
92 :
93 1873707 : if( FD_UNLIKELY( !sz ) ) return;
94 :
95 1645263 : ulong seq1 = seq0 + sz;
96 :
97 1645263 : ulong seq_past = mm->base->seq_past;
98 1645263 : ulong seq_present = mm->base->seq_present;
99 :
100 1645263 : int bad_seq = !fd_ulong_is_aligned( seq0, FD_VINYL_BSTREAM_BLOCK_SZ );
101 1645263 : int bad_dst = (!fd_ulong_is_aligned( (ulong)dst, FD_VINYL_BSTREAM_BLOCK_SZ )) | !dst;
102 1645263 : int bad_sz = !fd_ulong_is_aligned( sz, FD_VINYL_BSTREAM_BLOCK_SZ );
103 1645263 : int bad_past = !(fd_vinyl_seq_le( seq_past, seq0 ) & fd_vinyl_seq_lt( seq0, seq1 ) & fd_vinyl_seq_le( seq1, seq_present ));
104 :
105 1645263 : if( FD_UNLIKELY( bad_seq | bad_dst | bad_sz | bad_past ) )
106 0 : FD_LOG_CRIT(( "bstream read [%016lx,%016lx)/%lu failed (past [%016lx,%016lx)/%lu, %s)",
107 1645263 : seq0, seq1, sz, seq_past, seq_present, seq_present-seq_past,
108 1645263 : bad_seq ? "misaligned seq" :
109 1645263 : bad_dst ? "misaligned or NULL dst" :
110 1645263 : bad_sz ? "misaligned sz" :
111 1645263 : "not in past" ));
112 :
113 : /* At this point, we have a valid read request. Map seq0 into the
114 : bstream store. Read the lesser of sz bytes or until the store end.
115 : If we hit the store end with more to go, wrap around and finish the
116 : read at the store start. */
117 :
118 1645263 : uchar const * dev = mm->dev;
119 1645263 : ulong dev_base = mm->dev_base;
120 1645263 : ulong dev_sz = mm->dev_sz;
121 :
122 1645263 : ulong dev_off = seq0 % dev_sz;
123 :
124 1645263 : ulong rsz = fd_ulong_min( sz, dev_sz - dev_off );
125 1645263 : memcpy( dst, dev + dev_base + dev_off, rsz );
126 1645263 : sz -= rsz;
127 1645263 : if( FD_UNLIKELY( sz ) ) memcpy( dst + rsz, dev + dev_base, sz );
128 1645263 : }
129 :
130 : static int
131 : fd_vinyl_io_mm_poll( fd_vinyl_io_t * io,
132 : fd_vinyl_io_rd_t ** _rd,
133 3747414 : int flags ) {
134 3747414 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t * )io; /* Note: io must be non-NULL to have even been called */
135 3747414 : (void)flags;
136 :
137 3747414 : fd_vinyl_io_mm_rd_t * rd = mm->rd_head;
138 :
139 3747414 : if( FD_UNLIKELY( !rd ) ) {
140 1873707 : *_rd = NULL;
141 1873707 : return FD_VINYL_ERR_EMPTY;
142 1873707 : }
143 :
144 1873707 : fd_vinyl_io_mm_rd_t ** rd_tail_next = mm->rd_tail_next;
145 1873707 : fd_vinyl_io_mm_rd_t * rd_next = rd->next;
146 :
147 1873707 : mm->rd_head = rd_next;
148 1873707 : mm->rd_tail_next = fd_ptr_if( !!rd_next, rd_tail_next, &mm->rd_head );
149 :
150 1873707 : *_rd = (fd_vinyl_io_rd_t *)rd;
151 1873707 : return FD_VINYL_SUCCESS;
152 3747414 : }
153 :
154 : static ulong
155 : fd_vinyl_io_mm_append( fd_vinyl_io_t * io,
156 : void const * _src,
157 374868 : ulong sz ) {
158 374868 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
159 374868 : uchar const * src = (uchar const *)_src;
160 :
161 : /* Validate the input args. */
162 :
163 374868 : ulong seq_future = mm->base->seq_future; if( FD_UNLIKELY( !sz ) ) return seq_future;
164 374868 : ulong seq_ancient = mm->base->seq_ancient;
165 374868 : uchar * dev = mm->dev;
166 374868 : ulong dev_base = mm->dev_base;
167 374868 : ulong dev_sz = mm->dev_sz;
168 :
169 374868 : int bad_src = !src;
170 374868 : int bad_align = !fd_ulong_is_aligned( (ulong)src, FD_VINYL_BSTREAM_BLOCK_SZ );
171 374868 : int bad_sz = !fd_ulong_is_aligned( sz, FD_VINYL_BSTREAM_BLOCK_SZ );
172 374868 : int bad_capacity = sz > (dev_sz - (seq_future-seq_ancient));
173 :
174 374868 : if( FD_UNLIKELY( bad_src | bad_align | bad_sz | bad_capacity ) )
175 0 : FD_LOG_CRIT(( bad_src ? "NULL src" :
176 374868 : bad_align ? "misaligned src" :
177 374868 : bad_sz ? "misaligned sz" :
178 374868 : "device full" ));
179 :
180 : /* At this point, we appear to have a valid append request. Map it to
181 : the bstream (updating seq_future) and map it to the device. Then
182 : write the lesser of sz bytes or until the store end. If we hit the
183 : store end with more to go, wrap around and finish the write at the
184 : store start. */
185 :
186 374868 : ulong seq = seq_future;
187 374868 : mm->base->seq_future = seq + sz;
188 :
189 374868 : ulong dev_off = seq % dev_sz;
190 :
191 374868 : ulong wsz = fd_ulong_min( sz, dev_sz - dev_off );
192 374868 : memcpy( dev + dev_base + dev_off, src, wsz );
193 374868 : sz -= wsz;
194 374868 : if( sz ) memcpy( dev + dev_base, src + wsz, sz );
195 :
196 374868 : return seq;
197 374868 : }
198 :
199 : static int
200 : fd_vinyl_io_mm_commit( fd_vinyl_io_t * io,
201 374532 : int flags ) {
202 374532 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
203 374532 : (void)flags;
204 :
205 374532 : mm->base->seq_present = mm->base->seq_future;
206 374532 : mm->base->spad_used = 0UL;
207 :
208 374532 : return FD_VINYL_SUCCESS;
209 374532 : }
210 :
211 : static ulong
212 : fd_vinyl_io_mm_hint( fd_vinyl_io_t * io,
213 376362 : ulong sz ) {
214 376362 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
215 :
216 376362 : ulong seq_future = mm->base->seq_future; if( FD_UNLIKELY( !sz ) ) return seq_future;
217 376362 : ulong seq_ancient = mm->base->seq_ancient;
218 376362 : ulong dev_sz = mm->dev_sz;
219 :
220 376362 : int bad_sz = !fd_ulong_is_aligned( sz, FD_VINYL_BSTREAM_BLOCK_SZ );
221 376362 : int bad_capacity = sz > (dev_sz - (seq_future-seq_ancient));
222 :
223 376362 : if( FD_UNLIKELY( bad_sz | bad_capacity ) ) FD_LOG_CRIT(( bad_sz ? "misaligned sz" : "device full" ));
224 :
225 376362 : return mm->base->seq_future;
226 376362 : }
227 :
228 : static void *
229 : fd_vinyl_io_mm_alloc( fd_vinyl_io_t * io,
230 : ulong sz,
231 363 : int flags ) {
232 363 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
233 :
234 363 : ulong spad_max = mm->base->spad_max;
235 363 : ulong spad_used = mm->base->spad_used; if( FD_UNLIKELY( !sz ) ) return ((uchar *)(mm+1)) + spad_used;
236 :
237 363 : int bad_align = !fd_ulong_is_aligned( sz, FD_VINYL_BSTREAM_BLOCK_SZ );
238 363 : int bad_sz = sz > spad_max;
239 :
240 363 : if( FD_UNLIKELY( bad_align | bad_sz ) ) FD_LOG_CRIT(( bad_align ? "misaligned sz" : "sz too large" ));
241 :
242 363 : if( FD_UNLIKELY( sz > (spad_max - spad_used ) ) ) {
243 0 : if( FD_UNLIKELY( fd_vinyl_io_mm_commit( io, flags ) ) ) return NULL;
244 0 : spad_used = 0UL;
245 0 : }
246 :
247 363 : mm->base->spad_used = spad_used + sz;
248 :
249 363 : return ((uchar *)(mm+1)) + spad_used;
250 363 : }
251 :
252 : static ulong
253 : fd_vinyl_io_mm_copy( fd_vinyl_io_t * io,
254 : ulong seq_src0,
255 376227 : ulong sz ) {
256 376227 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
257 :
258 : /* Validate the input args */
259 :
260 376227 : ulong seq_ancient = mm->base->seq_ancient;
261 376227 : ulong seq_past = mm->base->seq_past;
262 376227 : ulong seq_present = mm->base->seq_present;
263 376227 : ulong seq_future = mm->base->seq_future; if( FD_UNLIKELY( !sz ) ) return seq_future;
264 330246 : ulong spad_max = mm->base->spad_max;
265 330246 : ulong spad_used = mm->base->spad_used;
266 330246 : uchar * dev = mm->dev;
267 330246 : ulong dev_base = mm->dev_base;
268 330246 : ulong dev_sz = mm->dev_sz;
269 :
270 330246 : ulong seq_src1 = seq_src0 + sz;
271 :
272 330246 : int bad_past = !( fd_vinyl_seq_le( seq_past, seq_src0 ) &
273 330246 : fd_vinyl_seq_lt( seq_src0, seq_src1 ) &
274 330246 : fd_vinyl_seq_le( seq_src1, seq_present ) );
275 330246 : int bad_src = !fd_ulong_is_aligned( seq_src0, FD_VINYL_BSTREAM_BLOCK_SZ );
276 330246 : int bad_sz = !fd_ulong_is_aligned( sz, FD_VINYL_BSTREAM_BLOCK_SZ );
277 330246 : int bad_capacity = sz > (dev_sz - (seq_future-seq_ancient));
278 :
279 330246 : if( FD_UNLIKELY( bad_past | bad_src | bad_sz | bad_capacity ) )
280 0 : FD_LOG_CRIT(( bad_past ? "src is not in the past" :
281 330246 : bad_src ? "misaligned src_seq" :
282 330246 : bad_sz ? "misaligned sz" :
283 330246 : "device full" ));
284 :
285 : /* At this point, we appear to have a valid copy request. Get
286 : buffer space from the scratch pad (committing as necessary). */
287 :
288 330246 : if( FD_UNLIKELY( sz>(spad_max-spad_used) ) ) {
289 0 : fd_vinyl_io_mm_commit( io, FD_VINYL_IO_FLAG_BLOCKING );
290 0 : spad_used = 0UL;
291 0 : }
292 :
293 330246 : uchar * buf = (uchar *)(mm+1) + spad_used;
294 330246 : ulong buf_max = spad_max - spad_used;
295 :
296 : /* Map the dst to the bstream (updating seq_future) and map the src
297 : and dst regions onto the device. Then copy as much as we can at a
298 : time, handling device wrap around and copy buffering space. */
299 :
300 330246 : ulong seq = seq_future;
301 330246 : mm->base->seq_future = seq + sz;
302 :
303 330246 : ulong seq_dst0 = seq;
304 :
305 330273 : for(;;) {
306 330273 : ulong src_off = seq_src0 % dev_sz;
307 330273 : ulong dst_off = seq_dst0 % dev_sz;
308 330273 : ulong csz = fd_ulong_min( fd_ulong_min( sz, buf_max ), fd_ulong_min( dev_sz - src_off, dev_sz - dst_off ) );
309 :
310 330273 : memcpy( buf, dev + dev_base + src_off, csz );
311 330273 : memcpy( dev + dev_base + dst_off, buf, csz );
312 :
313 330273 : sz -= csz;
314 330273 : if( !sz ) break;
315 :
316 27 : seq_src0 += csz;
317 27 : seq_dst0 += csz;
318 27 : }
319 :
320 330246 : return seq;
321 330246 : }
322 :
323 : static void
324 : fd_vinyl_io_mm_forget( fd_vinyl_io_t * io,
325 133263 : ulong seq ) {
326 133263 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
327 :
328 : /* Validate input arguments. Note that we don't allow forgetting into
329 : the future even when we have no uncommitted blocks because the
330 : resulting [seq_ancient,seq_future) might contain blocks that were
331 : never written (which might not be an issue practically but it would
332 : be a bit strange for something to try to scan starting from
333 : seq_ancient and discover unwritten blocks). */
334 :
335 133263 : ulong seq_past = mm->base->seq_past;
336 133263 : ulong seq_present = mm->base->seq_present;
337 133263 : ulong seq_future = mm->base->seq_future;
338 :
339 133263 : int bad_seq = !fd_ulong_is_aligned( seq, FD_VINYL_BSTREAM_BLOCK_SZ );
340 133263 : int bad_dir = !(fd_vinyl_seq_le( seq_past, seq ) & fd_vinyl_seq_le( seq, seq_present ));
341 133263 : int bad_read = !!mm->rd_head;
342 133263 : int bad_append = fd_vinyl_seq_ne( seq_present, seq_future );
343 :
344 133263 : if( FD_UNLIKELY( bad_seq | bad_dir | bad_read | bad_append ) )
345 0 : FD_LOG_CRIT(( "forget to seq %016lx failed (past [%016lx,%016lx)/%lu, %s)",
346 133263 : seq, seq_past, seq_present, seq_present-seq_past,
347 133263 : bad_seq ? "misaligned seq" :
348 133263 : bad_dir ? "seq out of bounds" :
349 133263 : bad_read ? "reads in progress" :
350 133263 : "appends/copies in progress" ));
351 :
352 133263 : mm->base->seq_past = seq;
353 133263 : }
354 :
355 : static void
356 : fd_vinyl_io_mm_rewind( fd_vinyl_io_t * io,
357 132108 : ulong seq ) {
358 132108 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
359 :
360 : /* Validate input argments. Unlike forgot, we do allow rewinding to
361 : before seq_ancient as the region of sequence space reported to the
362 : caller as written is still accurate. */
363 :
364 132108 : ulong seq_ancient = mm->base->seq_ancient;
365 132108 : ulong seq_past = mm->base->seq_past;
366 132108 : ulong seq_present = mm->base->seq_present;
367 132108 : ulong seq_future = mm->base->seq_future;
368 :
369 132108 : int bad_seq = !fd_ulong_is_aligned( seq, FD_VINYL_BSTREAM_BLOCK_SZ );
370 132108 : int bad_dir = fd_vinyl_seq_gt( seq, seq_present );
371 132108 : int bad_read = !!mm->rd_head;
372 132108 : int bad_append = fd_vinyl_seq_ne( seq_present, seq_future );
373 :
374 132108 : if( FD_UNLIKELY( bad_seq | bad_dir | bad_read | bad_append ) )
375 0 : FD_LOG_CRIT(( "rewind to seq %016lx failed (present %016lx, %s)", seq, seq_present,
376 132108 : bad_seq ? "misaligned seq" :
377 132108 : bad_dir ? "seq after seq_present" :
378 132108 : bad_read ? "reads in progress" :
379 132108 : "appends/copies in progress" ));
380 :
381 132108 : mm->base->seq_ancient = fd_ulong_if( fd_vinyl_seq_ge( seq, seq_ancient ), seq_ancient, seq );
382 132108 : mm->base->seq_past = fd_ulong_if( fd_vinyl_seq_ge( seq, seq_past ), seq_past, seq );
383 132108 : mm->base->seq_present = seq;
384 132108 : mm->base->seq_future = seq;
385 132108 : }
386 :
387 : static int
388 : fd_vinyl_io_mm_sync( fd_vinyl_io_t * io,
389 374442 : int flags ) {
390 374442 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
391 374442 : (void)flags;
392 :
393 374442 : ulong seed = mm->base->seed;
394 374442 : ulong seq_past = mm->base->seq_past;
395 374442 : ulong seq_present = mm->base->seq_present;
396 374442 : uchar * dev = mm->dev;
397 374442 : ulong dev_sync = mm->dev_sync;
398 :
399 374442 : fd_vinyl_bstream_block_t * block = mm->sync;
400 :
401 : /* block->sync.ctl current (static) */
402 374442 : block->sync.seq_past = seq_past;
403 374442 : block->sync.seq_present = seq_present;
404 : /* block->sync.info_sz current (static) */
405 : /* block->sync.info current (static) */
406 :
407 374442 : block->sync.hash_trail = 0UL;
408 374442 : block->sync.hash_blocks = 0UL;
409 374442 : fd_vinyl_bstream_block_hash( seed, block ); /* sets hash_trail back to seed */
410 :
411 374442 : memcpy( dev + dev_sync, block, FD_VINYL_BSTREAM_BLOCK_SZ );
412 :
413 374442 : mm->base->seq_ancient = seq_past;
414 :
415 374442 : return FD_VINYL_SUCCESS;
416 374442 : }
417 :
418 : static void *
419 6 : fd_vinyl_io_mm_fini( fd_vinyl_io_t * io ) {
420 6 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
421 :
422 6 : ulong seq_present = mm->base->seq_present;
423 6 : ulong seq_future = mm->base->seq_future;
424 :
425 6 : if( FD_UNLIKELY( mm->rd_head ) ) FD_LOG_WARNING(( "fini completing outstanding reads" ));
426 6 : if( FD_UNLIKELY( fd_vinyl_seq_ne( seq_present, seq_future ) ) ) FD_LOG_WARNING(( "fini discarding uncommited blocks" ));
427 :
428 6 : return io;
429 6 : }
430 :
431 : static fd_vinyl_io_impl_t fd_vinyl_io_mm_impl[1] = { {
432 : fd_vinyl_io_mm_read_imm,
433 : fd_vinyl_io_mm_read,
434 : fd_vinyl_io_mm_poll,
435 : fd_vinyl_io_mm_append,
436 : fd_vinyl_io_mm_commit,
437 : fd_vinyl_io_mm_hint,
438 : fd_vinyl_io_mm_alloc,
439 : fd_vinyl_io_mm_copy,
440 : fd_vinyl_io_mm_forget,
441 : fd_vinyl_io_mm_rewind,
442 : fd_vinyl_io_mm_sync,
443 : fd_vinyl_io_mm_fini
444 : } };
445 :
446 : FD_STATIC_ASSERT( alignof(fd_vinyl_io_mm_t)==FD_VINYL_BSTREAM_BLOCK_SZ, layout );
447 :
448 : ulong
449 39 : fd_vinyl_io_mm_align( void ) {
450 39 : return alignof(fd_vinyl_io_mm_t);
451 39 : }
452 :
453 : ulong
454 39 : fd_vinyl_io_mm_footprint( ulong spad_max ) {
455 39 : if( FD_UNLIKELY( !((0UL<spad_max) & (spad_max<(1UL<<63)) & fd_ulong_is_aligned( spad_max, FD_VINYL_BSTREAM_BLOCK_SZ )) ) )
456 12 : return 0UL;
457 27 : return sizeof(fd_vinyl_io_mm_t) + spad_max;
458 39 : }
459 :
460 : fd_vinyl_io_t *
461 : fd_vinyl_io_mm_init( void * mem,
462 : ulong spad_max,
463 : void * dev,
464 : ulong dev_sz,
465 : int reset,
466 : void const * info,
467 : ulong info_sz,
468 39 : ulong io_seed ) {
469 39 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)mem;
470 :
471 39 : if( FD_UNLIKELY( !mm ) ) {
472 3 : FD_LOG_WARNING(( "NULL mem" ));
473 3 : return NULL;
474 3 : }
475 :
476 36 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mm, fd_vinyl_io_mm_align() ) ) ) {
477 3 : FD_LOG_WARNING(( "misaligned mem" ));
478 3 : return NULL;
479 3 : }
480 :
481 33 : ulong footprint = fd_vinyl_io_mm_footprint( spad_max );
482 33 : if( FD_UNLIKELY( !footprint ) ) {
483 9 : FD_LOG_WARNING(( "bad spad_max" ));
484 9 : return NULL;
485 9 : }
486 :
487 24 : if( FD_UNLIKELY( !dev ) ) {
488 3 : FD_LOG_WARNING(( "NULL dev" ));
489 3 : return NULL;
490 3 : }
491 :
492 21 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)dev, FD_VINYL_BSTREAM_BLOCK_SZ ) ) ) {
493 3 : FD_LOG_WARNING(( "misaligned mem" ));
494 3 : return NULL;
495 3 : }
496 :
497 18 : ulong dev_sz_min = 3UL*FD_VINYL_BSTREAM_BLOCK_SZ /* sync block, move block, closing partition */
498 18 : + fd_vinyl_bstream_pair_sz( FD_VINYL_VAL_MAX ); /* worst case pair (FIXME: LZ4_COMPRESSBOUND?) */
499 :
500 18 : int too_small = dev_sz < dev_sz_min;
501 18 : int too_large = dev_sz > (ulong)LONG_MAX;
502 18 : int misaligned = !fd_ulong_is_aligned( dev_sz, FD_VINYL_BSTREAM_BLOCK_SZ );
503 :
504 18 : if( FD_UNLIKELY( too_small | too_large | misaligned ) ) {
505 6 : FD_LOG_WARNING(( "bstream size %s", too_small ? "too small" :
506 6 : too_large ? "too large" :
507 6 : "not a block size multiple" ));
508 6 : return NULL;
509 6 : }
510 :
511 12 : if( reset ) {
512 6 : if( FD_UNLIKELY( !info ) ) info_sz = 0UL;
513 6 : if( FD_UNLIKELY( info_sz>FD_VINYL_BSTREAM_SYNC_INFO_MAX ) ) {
514 3 : FD_LOG_WARNING(( "info_sz too large" ));
515 3 : return NULL;
516 3 : }
517 6 : }
518 :
519 9 : memset( mm, 0, footprint );
520 :
521 9 : mm->base->type = FD_VINYL_IO_TYPE_MM;
522 :
523 : /* io_seed, seq_ancient, seq_past, seq_present, seq_future are init
524 : below */
525 :
526 9 : mm->base->spad_max = spad_max;
527 9 : mm->base->spad_used = 0UL;
528 9 : mm->base->impl = fd_vinyl_io_mm_impl;
529 :
530 9 : mm->dev = dev;
531 9 : mm->dev_sync = 0UL; /* Use the beginning of the file for the sync block */
532 9 : mm->dev_base = FD_VINYL_BSTREAM_BLOCK_SZ; /* Use the rest for the actual bstream store (at least 4) */
533 9 : mm->dev_sz = dev_sz - FD_VINYL_BSTREAM_BLOCK_SZ;
534 :
535 9 : mm->rd_head = NULL;
536 9 : mm->rd_tail_next = &mm->rd_head;
537 :
538 : /* Note that [seq_ancient,seq_future) (cyclic) contains at most dev_sz
539 : bytes, bstream's antiquity, past and present are subsets of this
540 : range and dev_sz is less than 2^63 given the above (practically
541 : much much less). As such, differences between two ordered bstream
542 : sequence numbers (e.g. ulong sz = seq_a - seq_b where a is
543 : logically not before b) will "just work" regardless of wrapping
544 : and/or amount of data stored. */
545 :
546 9 : fd_vinyl_bstream_block_t * block = mm->sync;
547 :
548 9 : if( reset ) {
549 :
550 : /* We are starting a new bstream. Write the initial sync block. */
551 :
552 3 : mm->base->seed = io_seed;
553 3 : mm->base->seq_ancient = 0UL;
554 3 : mm->base->seq_past = 0UL;
555 3 : mm->base->seq_present = 0UL;
556 3 : mm->base->seq_future = 0UL;
557 :
558 3 : memset( block, 0, FD_VINYL_BSTREAM_BLOCK_SZ ); /* bulk zero */
559 :
560 3 : block->sync.ctl = fd_vinyl_bstream_ctl( FD_VINYL_BSTREAM_CTL_TYPE_SYNC, 0, FD_VINYL_VAL_MAX );
561 : //block->sync.seq_past = ...; /* init by sync */
562 : //block->sync.seq_present = ...; /* init by sync */
563 3 : block->sync.info_sz = info_sz;
564 3 : if( info_sz ) memcpy( block->sync.info, info, info_sz );
565 : //block->sync.hash_trail = ...; /* init by sync */
566 : //block->sync.hash_blocks = ...; /* init by sync */
567 :
568 3 : int err = fd_vinyl_io_mm_sync( mm->base, FD_VINYL_IO_FLAG_BLOCKING ); /* logs details */
569 3 : if( FD_UNLIKELY( err ) ) {
570 0 : FD_LOG_WARNING(( "sync block write failed (%i-%s)", err, fd_vinyl_strerror( err ) ));
571 0 : return NULL;
572 0 : }
573 :
574 6 : } else {
575 :
576 : /* We are resuming an existing bstream. Read and validate the
577 : bstream's sync block. */
578 :
579 6 : memcpy( block, mm->dev + mm->dev_sync, FD_VINYL_BSTREAM_BLOCK_SZ ); /* logs details */
580 :
581 6 : int type = fd_vinyl_bstream_ctl_type ( block->sync.ctl );
582 6 : int version = fd_vinyl_bstream_ctl_style( block->sync.ctl );
583 6 : ulong val_max = fd_vinyl_bstream_ctl_sz ( block->sync.ctl );
584 6 : ulong seq_past = block->sync.seq_past;
585 6 : ulong seq_present = block->sync.seq_present;
586 6 : /**/ info_sz = block->sync.info_sz; // overrides user info_sz
587 6 : /**/ info = block->sync.info; // overrides user info
588 6 : /**/ io_seed = block->sync.hash_trail; // overrides user io_seed
589 :
590 6 : int bad_type = (type != FD_VINYL_BSTREAM_CTL_TYPE_SYNC);
591 6 : int bad_version = (version != 0);
592 6 : int bad_val_max = (val_max != FD_VINYL_VAL_MAX);
593 6 : int bad_seq_past = !fd_ulong_is_aligned( seq_past, FD_VINYL_BSTREAM_BLOCK_SZ );
594 6 : int bad_seq_present = !fd_ulong_is_aligned( seq_present, FD_VINYL_BSTREAM_BLOCK_SZ );
595 6 : int bad_info_sz = (info_sz > FD_VINYL_BSTREAM_SYNC_INFO_MAX);
596 6 : int bad_past_order = fd_vinyl_seq_gt( seq_past, seq_present );
597 6 : int bad_past_sz = ((seq_present-seq_past) > mm->dev_sz);
598 :
599 6 : if( FD_UNLIKELY( bad_type | bad_version | bad_val_max | bad_seq_past | bad_seq_present | bad_info_sz |
600 6 : bad_past_order | bad_past_sz ) ) {
601 3 : FD_LOG_WARNING(( "bad sync block when recovering (%s)",
602 3 : bad_type ? "unexpected type" :
603 3 : bad_version ? "unexpected version" :
604 3 : bad_val_max ? "unexpected max pair value decoded byte size" :
605 3 : bad_seq_past ? "unaligned seq_past" :
606 3 : bad_seq_present ? "unaligned seq_present" :
607 3 : bad_info_sz ? "unexpected info size" :
608 3 : bad_past_order ? "unordered seq_past and seq_present" :
609 3 : "past size larger than bstream store" ));
610 3 : return NULL;
611 3 : }
612 :
613 3 : if( FD_UNLIKELY( fd_vinyl_bstream_block_test( io_seed, block ) ) ) {
614 0 : FD_LOG_WARNING(( "corrupt sync block when recovering bstream store" ));
615 0 : return NULL;
616 0 : }
617 :
618 3 : mm->base->seed = io_seed;
619 3 : mm->base->seq_ancient = seq_past;
620 3 : mm->base->seq_past = seq_past;
621 3 : mm->base->seq_present = seq_present;
622 3 : mm->base->seq_future = seq_present;
623 :
624 3 : }
625 :
626 6 : FD_LOG_NOTICE(( "IO config"
627 6 : "\n\ttype mm"
628 6 : "\n\tspad_max %lu bytes"
629 6 : "\n\tdev_sz %lu bytes"
630 6 : "\n\treset %i"
631 6 : "\n\tinfo \"%s\" (info_sz %lu%s)"
632 6 : "\n\tio_seed 0x%016lx%s",
633 6 : spad_max, dev_sz, reset,
634 6 : info ? (char const *)info : "", info_sz, reset ? "" : ", discovered",
635 6 : io_seed, reset ? "" : " (discovered)" ));
636 :
637 6 : return mm->base;
638 9 : }
639 :
640 : void *
641 6 : fd_vinyl_mmio( fd_vinyl_io_t * io ) {
642 6 : if( FD_UNLIKELY( io->type!=FD_VINYL_IO_TYPE_MM ) ) return NULL;
643 3 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io;
644 3 : return mm->dev + mm->dev_base;
645 6 : }
646 :
647 : ulong
648 6 : fd_vinyl_mmio_sz( fd_vinyl_io_t * io ) {
649 6 : if( FD_UNLIKELY( io->type!=FD_VINYL_IO_TYPE_MM ) ) return 0UL;
650 3 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io;
651 3 : return mm->dev_sz;
652 6 : }
|