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