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 1499367 : ulong sz ) {
33 1499367 : 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 1499367 : if( FD_UNLIKELY( !sz ) ) return;
39 :
40 1351080 : uchar * dst = (uchar *)_dst;
41 1351080 : ulong seq1 = seq0 + sz;
42 :
43 1351080 : ulong seq_past = mm->base->seq_past;
44 1351080 : ulong seq_present = mm->base->seq_present;
45 :
46 1351080 : int bad_seq = !fd_ulong_is_aligned( seq0, FD_VINYL_BSTREAM_BLOCK_SZ );
47 1351080 : int bad_dst = !dst;
48 1351080 : int bad_sz = !fd_ulong_is_aligned( sz, FD_VINYL_BSTREAM_BLOCK_SZ );
49 1351080 : 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 1351080 : 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 1351080 : seq0, seq1, sz, seq_past, seq_present, seq_present-seq_past,
54 1351080 : bad_seq ? "misaligned seq" :
55 1351080 : bad_dst ? "NULL dst" :
56 1351080 : bad_sz ? "misaligned sz" :
57 1351080 : "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 1351080 : uchar * dev = mm->dev;
65 1351080 : ulong dev_base = mm->dev_base;
66 1351080 : ulong dev_sz = mm->dev_sz;
67 :
68 1351080 : ulong dev_off = seq0 % dev_sz;
69 :
70 1351080 : ulong rsz = fd_ulong_min( sz, dev_sz - dev_off );
71 1351080 : memcpy( dst, dev + dev_base + dev_off, rsz );
72 1351080 : io->cache_read_tot_sz += rsz;
73 1351080 : sz -= rsz;
74 1351080 : if( FD_UNLIKELY( sz ) ) memcpy( dst + rsz, dev + dev_base, sz );
75 1351080 : io->cache_read_tot_sz += sz;
76 1351080 : }
77 :
78 : static void
79 : fd_vinyl_io_mm_read( fd_vinyl_io_t * io,
80 1874070 : fd_vinyl_io_rd_t * _rd ) {
81 1874070 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *) io; /* Note: io must be non-NULL to have even been called */
82 1874070 : fd_vinyl_io_mm_rd_t * rd = (fd_vinyl_io_mm_rd_t *)_rd;
83 :
84 1874070 : rd->next = NULL;
85 1874070 : *mm->rd_tail_next = rd;
86 1874070 : mm->rd_tail_next = &rd->next;
87 :
88 1874070 : ulong seq0 = rd->seq;
89 1874070 : uchar * dst = (uchar *)rd->dst;
90 1874070 : 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 1874070 : if( FD_UNLIKELY( !sz ) ) return;
96 :
97 1689405 : ulong seq1 = seq0 + sz;
98 :
99 1689405 : ulong seq_past = mm->base->seq_past;
100 1689405 : ulong seq_present = mm->base->seq_present;
101 :
102 1689405 : int bad_seq = !fd_ulong_is_aligned( seq0, FD_VINYL_BSTREAM_BLOCK_SZ );
103 1689405 : int bad_dst = !dst;
104 1689405 : int bad_sz = !fd_ulong_is_aligned( sz, FD_VINYL_BSTREAM_BLOCK_SZ );
105 1689405 : 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 1689405 : 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 1689405 : seq0, seq1, sz, seq_past, seq_present, seq_present-seq_past,
110 1689405 : bad_seq ? "misaligned seq" :
111 1689405 : bad_dst ? "NULL dst" :
112 1689405 : bad_sz ? "misaligned sz" :
113 1689405 : "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 1689405 : uchar const * dev = mm->dev;
121 1689405 : ulong dev_base = mm->dev_base;
122 1689405 : ulong dev_sz = mm->dev_sz;
123 :
124 1689405 : ulong dev_off = seq0 % dev_sz;
125 :
126 1689405 : ulong rsz = fd_ulong_min( sz, dev_sz - dev_off );
127 1689405 : memcpy( dst, dev + dev_base + dev_off, rsz );
128 1689405 : io->cache_read_tot_sz += rsz;
129 1689405 : sz -= rsz;
130 1689405 : if( FD_UNLIKELY( sz ) ) memcpy( dst + rsz, dev + dev_base, sz );
131 1689405 : io->cache_read_tot_sz += sz;
132 1689405 : }
133 :
134 : static int
135 : fd_vinyl_io_mm_poll( fd_vinyl_io_t * io,
136 : fd_vinyl_io_rd_t ** _rd,
137 3748140 : int flags ) {
138 3748140 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t * )io; /* Note: io must be non-NULL to have even been called */
139 3748140 : (void)flags;
140 :
141 3748140 : fd_vinyl_io_mm_rd_t * rd = mm->rd_head;
142 :
143 3748140 : if( FD_UNLIKELY( !rd ) ) {
144 1874070 : *_rd = NULL;
145 1874070 : return FD_VINYL_ERR_EMPTY;
146 1874070 : }
147 :
148 1874070 : fd_vinyl_io_mm_rd_t ** rd_tail_next = mm->rd_tail_next;
149 1874070 : fd_vinyl_io_mm_rd_t * rd_next = rd->next;
150 :
151 1874070 : mm->rd_head = rd_next;
152 1874070 : mm->rd_tail_next = fd_ptr_if( !!rd_next, rd_tail_next, &mm->rd_head );
153 :
154 1874070 : *_rd = (fd_vinyl_io_rd_t *)rd;
155 1874070 : return FD_VINYL_SUCCESS;
156 3748140 : }
157 :
158 : static ulong
159 : fd_vinyl_io_mm_append( fd_vinyl_io_t * io,
160 : void const * _src,
161 375105 : ulong sz ) {
162 375105 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
163 375105 : uchar const * src = (uchar const *)_src;
164 :
165 : /* Validate the input args. */
166 :
167 375105 : ulong seq_future = mm->base->seq_future; if( FD_UNLIKELY( !sz ) ) return seq_future;
168 375105 : ulong seq_ancient = mm->base->seq_ancient;
169 375105 : uchar * dev = mm->dev;
170 375105 : ulong dev_base = mm->dev_base;
171 375105 : ulong dev_sz = mm->dev_sz;
172 :
173 375105 : int bad_src = !src;
174 375105 : int bad_align = !fd_ulong_is_aligned( (ulong)src, FD_VINYL_BSTREAM_BLOCK_SZ );
175 375105 : int bad_sz = !fd_ulong_is_aligned( sz, FD_VINYL_BSTREAM_BLOCK_SZ );
176 375105 : int bad_capacity = sz > (dev_sz - (seq_future-seq_ancient));
177 :
178 375105 : if( FD_UNLIKELY( bad_src | bad_align | bad_sz | bad_capacity ) )
179 0 : FD_LOG_CRIT(( bad_src ? "NULL src" :
180 375105 : bad_align ? "misaligned src" :
181 375105 : bad_sz ? "misaligned sz" :
182 375105 : "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 375105 : ulong seq = seq_future;
191 375105 : mm->base->seq_future = seq + sz;
192 :
193 375105 : ulong dev_off = seq % dev_sz;
194 :
195 375105 : ulong wsz = fd_ulong_min( sz, dev_sz - dev_off );
196 375105 : memcpy( dev + dev_base + dev_off, src, wsz );
197 375105 : io->file_write_tot_sz += wsz;
198 375105 : sz -= wsz;
199 375105 : if( sz ) memcpy( dev + dev_base, src + wsz, sz );
200 375105 : io->file_write_tot_sz += sz;
201 :
202 375105 : return seq;
203 375105 : }
204 :
205 : static int
206 : fd_vinyl_io_mm_commit( fd_vinyl_io_t * io,
207 374502 : int flags ) {
208 374502 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
209 374502 : (void)flags;
210 :
211 374502 : mm->base->seq_present = mm->base->seq_future;
212 374502 : mm->base->spad_used = 0UL;
213 :
214 374502 : return FD_VINYL_SUCCESS;
215 374502 : }
216 :
217 : static ulong
218 : fd_vinyl_io_mm_hint( fd_vinyl_io_t * io,
219 376722 : ulong sz ) {
220 376722 : 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 376722 : ulong seq_future = mm->base->seq_future; if( FD_UNLIKELY( !sz ) ) return seq_future;
223 376722 : ulong seq_ancient = mm->base->seq_ancient;
224 376722 : ulong dev_sz = mm->dev_sz;
225 :
226 376722 : int bad_sz = !fd_ulong_is_aligned( sz, FD_VINYL_BSTREAM_BLOCK_SZ );
227 376722 : int bad_capacity = sz > (dev_sz - (seq_future-seq_ancient));
228 :
229 376722 : if( FD_UNLIKELY( bad_sz | bad_capacity ) ) FD_LOG_CRIT(( bad_sz ? "misaligned sz" : "device full" ));
230 :
231 376722 : return mm->base->seq_future;
232 376722 : }
233 :
234 : static void *
235 : fd_vinyl_io_mm_alloc( fd_vinyl_io_t * io,
236 : ulong sz,
237 1482 : int flags ) {
238 1482 : 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 1482 : ulong spad_max = mm->base->spad_max;
241 1482 : ulong spad_used = mm->base->spad_used; if( FD_UNLIKELY( !sz ) ) return ((uchar *)(mm+1)) + spad_used;
242 :
243 1482 : int bad_align = !fd_ulong_is_aligned( sz, FD_VINYL_BSTREAM_BLOCK_SZ );
244 1482 : int bad_sz = sz > spad_max;
245 :
246 1482 : if( FD_UNLIKELY( bad_align | bad_sz ) ) FD_LOG_CRIT(( bad_align ? "misaligned sz" : "sz too large" ));
247 :
248 1482 : 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 1482 : mm->base->spad_used = spad_used + sz;
254 :
255 1482 : return ((uchar *)(mm+1)) + spad_used;
256 1482 : }
257 :
258 : static ulong
259 : fd_vinyl_io_mm_copy( fd_vinyl_io_t * io,
260 : ulong seq_src0,
261 376149 : ulong sz ) {
262 376149 : 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 376149 : ulong seq_ancient = mm->base->seq_ancient;
267 376149 : ulong seq_past = mm->base->seq_past;
268 376149 : ulong seq_present = mm->base->seq_present;
269 376149 : ulong seq_future = mm->base->seq_future; if( FD_UNLIKELY( !sz ) ) return seq_future;
270 339045 : ulong spad_max = mm->base->spad_max;
271 339045 : ulong spad_used = mm->base->spad_used;
272 339045 : uchar * dev = mm->dev;
273 339045 : ulong dev_base = mm->dev_base;
274 339045 : ulong dev_sz = mm->dev_sz;
275 :
276 339045 : ulong seq_src1 = seq_src0 + sz;
277 :
278 339045 : int bad_past = !( fd_vinyl_seq_le( seq_past, seq_src0 ) &
279 339045 : fd_vinyl_seq_lt( seq_src0, seq_src1 ) &
280 339045 : fd_vinyl_seq_le( seq_src1, seq_present ) );
281 339045 : int bad_src = !fd_ulong_is_aligned( seq_src0, FD_VINYL_BSTREAM_BLOCK_SZ );
282 339045 : int bad_sz = !fd_ulong_is_aligned( sz, FD_VINYL_BSTREAM_BLOCK_SZ );
283 339045 : int bad_capacity = sz > (dev_sz - (seq_future-seq_ancient));
284 :
285 339045 : 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 339045 : bad_src ? "misaligned src_seq" :
288 339045 : bad_sz ? "misaligned sz" :
289 339045 : "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 339045 : 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 339045 : uchar * buf = (uchar *)(mm+1) + spad_used;
300 339045 : 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 339045 : ulong seq = seq_future;
307 339045 : mm->base->seq_future = seq + sz;
308 :
309 339045 : ulong seq_dst0 = seq;
310 :
311 339048 : for(;;) {
312 339048 : ulong src_off = seq_src0 % dev_sz;
313 339048 : ulong dst_off = seq_dst0 % dev_sz;
314 339048 : ulong csz = fd_ulong_min( fd_ulong_min( sz, buf_max ), fd_ulong_min( dev_sz - src_off, dev_sz - dst_off ) );
315 :
316 339048 : memcpy( buf, dev + dev_base + src_off, csz );
317 339048 : memcpy( dev + dev_base + dst_off, buf, csz );
318 339048 : io->file_read_tot_sz += csz;
319 339048 : io->file_write_tot_sz += csz;
320 :
321 339048 : sz -= csz;
322 339048 : if( !sz ) break;
323 :
324 3 : seq_src0 += csz;
325 3 : seq_dst0 += csz;
326 3 : }
327 :
328 339045 : return seq;
329 339045 : }
330 :
331 : static void
332 : fd_vinyl_io_mm_forget( fd_vinyl_io_t * io,
333 131763 : ulong seq ) {
334 131763 : 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 131763 : ulong seq_past = mm->base->seq_past;
344 131763 : ulong seq_present = mm->base->seq_present;
345 131763 : ulong seq_future = mm->base->seq_future;
346 :
347 131763 : int bad_seq = !fd_ulong_is_aligned( seq, FD_VINYL_BSTREAM_BLOCK_SZ );
348 131763 : int bad_dir = !(fd_vinyl_seq_le( seq_past, seq ) & fd_vinyl_seq_le( seq, seq_present ));
349 131763 : int bad_read = !!mm->rd_head;
350 131763 : int bad_append = fd_vinyl_seq_ne( seq_present, seq_future );
351 :
352 131763 : 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 131763 : seq, seq_past, seq_present, seq_present-seq_past,
355 131763 : bad_seq ? "misaligned seq" :
356 131763 : bad_dir ? "seq out of bounds" :
357 131763 : bad_read ? "reads in progress" :
358 131763 : "appends/copies in progress" ));
359 :
360 131763 : mm->base->seq_past = seq;
361 131763 : }
362 :
363 : static void
364 : fd_vinyl_io_mm_rewind( fd_vinyl_io_t * io,
365 106734 : ulong seq ) {
366 106734 : 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 106734 : ulong seq_ancient = mm->base->seq_ancient;
373 106734 : ulong seq_past = mm->base->seq_past;
374 106734 : ulong seq_present = mm->base->seq_present;
375 106734 : ulong seq_future = mm->base->seq_future;
376 :
377 106734 : int bad_seq = !fd_ulong_is_aligned( seq, FD_VINYL_BSTREAM_BLOCK_SZ );
378 106734 : int bad_dir = fd_vinyl_seq_gt( seq, seq_present );
379 106734 : int bad_read = !!mm->rd_head;
380 106734 : int bad_append = fd_vinyl_seq_ne( seq_present, seq_future );
381 :
382 106734 : 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 106734 : bad_seq ? "misaligned seq" :
385 106734 : bad_dir ? "seq after seq_present" :
386 106734 : bad_read ? "reads in progress" :
387 106734 : "appends/copies in progress" ));
388 :
389 106734 : mm->base->seq_ancient = fd_ulong_if( fd_vinyl_seq_ge( seq, seq_ancient ), seq_ancient, seq );
390 106734 : mm->base->seq_past = fd_ulong_if( fd_vinyl_seq_ge( seq, seq_past ), seq_past, seq );
391 106734 : mm->base->seq_present = seq;
392 106734 : mm->base->seq_future = seq;
393 106734 : }
394 :
395 : static int
396 : fd_vinyl_io_mm_sync( fd_vinyl_io_t * io,
397 374163 : int flags ) {
398 374163 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io; /* Note: io must be non-NULL to have even been called */
399 374163 : (void)flags;
400 :
401 374163 : ulong seed = mm->base->seed;
402 374163 : ulong seq_past = mm->base->seq_past;
403 374163 : ulong seq_present = mm->base->seq_present;
404 374163 : uchar * dev = mm->dev;
405 374163 : ulong dev_sync = mm->dev_sync;
406 :
407 374163 : fd_vinyl_bstream_block_t * block = mm->sync;
408 :
409 : /* block->sync.ctl current (static) */
410 374163 : block->sync.seq_past = seq_past;
411 374163 : block->sync.seq_present = seq_present;
412 : /* block->sync.info_sz current (static) */
413 : /* block->sync.info current (static) */
414 :
415 374163 : block->sync.hash_trail = 0UL;
416 374163 : block->sync.hash_blocks = 0UL;
417 374163 : fd_vinyl_bstream_block_hash( seed, block ); /* sets hash_trail back to seed */
418 :
419 374163 : memcpy( dev + dev_sync, block, FD_VINYL_BSTREAM_BLOCK_SZ );
420 374163 : io->file_write_tot_sz += FD_VINYL_BSTREAM_BLOCK_SZ;
421 :
422 374163 : mm->base->seq_ancient = seq_past;
423 :
424 374163 : return FD_VINYL_SUCCESS;
425 374163 : }
426 :
427 : static void *
428 6 : fd_vinyl_io_mm_fini( fd_vinyl_io_t * io ) {
429 6 : 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 6 : ulong seq_present = mm->base->seq_present;
432 6 : ulong seq_future = mm->base->seq_future;
433 :
434 6 : if( FD_UNLIKELY( mm->rd_head ) ) FD_LOG_WARNING(( "fini completing outstanding reads" ));
435 6 : if( FD_UNLIKELY( fd_vinyl_seq_ne( seq_present, seq_future ) ) ) FD_LOG_WARNING(( "fini discarding uncommited blocks" ));
436 :
437 6 : return io;
438 6 : }
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 39 : fd_vinyl_io_mm_align( void ) {
459 39 : return alignof(fd_vinyl_io_mm_t);
460 39 : }
461 :
462 : ulong
463 39 : fd_vinyl_io_mm_footprint( ulong spad_max ) {
464 39 : if( FD_UNLIKELY( !((0UL<spad_max) & (spad_max<(1UL<<63)) & fd_ulong_is_aligned( spad_max, FD_VINYL_BSTREAM_BLOCK_SZ )) ) )
465 12 : return 0UL;
466 27 : return sizeof(fd_vinyl_io_mm_t) + spad_max;
467 39 : }
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 39 : ulong io_seed ) {
478 39 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)mem;
479 :
480 39 : if( FD_UNLIKELY( !mm ) ) {
481 3 : FD_LOG_WARNING(( "NULL mem" ));
482 3 : return NULL;
483 3 : }
484 :
485 36 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mm, fd_vinyl_io_mm_align() ) ) ) {
486 3 : FD_LOG_WARNING(( "misaligned mem" ));
487 3 : return NULL;
488 3 : }
489 :
490 33 : ulong footprint = fd_vinyl_io_mm_footprint( spad_max );
491 33 : if( FD_UNLIKELY( !footprint ) ) {
492 9 : FD_LOG_WARNING(( "bad spad_max" ));
493 9 : return NULL;
494 9 : }
495 :
496 24 : if( FD_UNLIKELY( !dev ) ) {
497 3 : FD_LOG_WARNING(( "NULL dev" ));
498 3 : return NULL;
499 3 : }
500 :
501 21 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)dev, FD_VINYL_BSTREAM_BLOCK_SZ ) ) ) {
502 3 : FD_LOG_WARNING(( "misaligned mem" ));
503 3 : return NULL;
504 3 : }
505 :
506 18 : ulong dev_sz_min = 3UL*FD_VINYL_BSTREAM_BLOCK_SZ /* sync block, move block, closing partition */
507 18 : + fd_vinyl_bstream_pair_sz( FD_VINYL_VAL_MAX ); /* worst case pair (FIXME: LZ4_COMPRESSBOUND?) */
508 :
509 18 : int too_small = dev_sz < dev_sz_min;
510 18 : int too_large = dev_sz > (ulong)LONG_MAX;
511 18 : int misaligned = !fd_ulong_is_aligned( dev_sz, FD_VINYL_BSTREAM_BLOCK_SZ );
512 :
513 18 : if( FD_UNLIKELY( too_small | too_large | misaligned ) ) {
514 6 : FD_LOG_WARNING(( "bstream size %s", too_small ? "too small" :
515 6 : too_large ? "too large" :
516 6 : "not a block size multiple" ));
517 6 : return NULL;
518 6 : }
519 :
520 12 : if( reset ) {
521 6 : if( FD_UNLIKELY( !info ) ) info_sz = 0UL;
522 6 : if( FD_UNLIKELY( info_sz>FD_VINYL_BSTREAM_SYNC_INFO_MAX ) ) {
523 3 : FD_LOG_WARNING(( "info_sz too large" ));
524 3 : return NULL;
525 3 : }
526 6 : }
527 :
528 9 : memset( mm, 0, footprint );
529 :
530 9 : 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 9 : mm->base->spad_max = spad_max;
536 9 : mm->base->spad_used = 0UL;
537 9 : mm->base->impl = fd_vinyl_io_mm_impl;
538 :
539 9 : mm->dev = dev;
540 9 : mm->dev_sync = 0UL; /* Use the beginning of the file for the sync block */
541 9 : mm->dev_base = FD_VINYL_BSTREAM_BLOCK_SZ; /* Use the rest for the actual bstream store (at least 4) */
542 9 : mm->dev_sz = dev_sz - FD_VINYL_BSTREAM_BLOCK_SZ;
543 :
544 9 : mm->rd_head = NULL;
545 9 : 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 9 : fd_vinyl_bstream_block_t * block = mm->sync;
556 :
557 9 : if( reset ) {
558 :
559 : /* We are starting a new bstream. Write the initial sync block. */
560 :
561 3 : mm->base->seed = io_seed;
562 3 : mm->base->seq_ancient = 0UL;
563 3 : mm->base->seq_past = 0UL;
564 3 : mm->base->seq_present = 0UL;
565 3 : mm->base->seq_future = 0UL;
566 :
567 3 : memset( block, 0, FD_VINYL_BSTREAM_BLOCK_SZ ); /* bulk zero */
568 :
569 3 : 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 3 : block->sync.info_sz = info_sz;
573 3 : 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 3 : int err = fd_vinyl_io_mm_sync( mm->base, FD_VINYL_IO_FLAG_BLOCKING ); /* logs details */
578 3 : 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 6 : } else {
584 :
585 : /* We are resuming an existing bstream. Read and validate the
586 : bstream's sync block. */
587 :
588 6 : memcpy( block, mm->dev + mm->dev_sync, FD_VINYL_BSTREAM_BLOCK_SZ ); /* logs details */
589 :
590 6 : int type = fd_vinyl_bstream_ctl_type ( block->sync.ctl );
591 6 : int version = fd_vinyl_bstream_ctl_style( block->sync.ctl );
592 6 : ulong val_max = fd_vinyl_bstream_ctl_sz ( block->sync.ctl );
593 6 : ulong seq_past = block->sync.seq_past;
594 6 : ulong seq_present = block->sync.seq_present;
595 6 : /**/ info_sz = block->sync.info_sz; // overrides user info_sz
596 6 : /**/ info = block->sync.info; // overrides user info
597 6 : /**/ io_seed = block->sync.hash_trail; // overrides user io_seed
598 :
599 6 : int bad_type = (type != FD_VINYL_BSTREAM_CTL_TYPE_SYNC);
600 6 : int bad_version = (version != 0);
601 6 : int bad_val_max = (val_max != FD_VINYL_VAL_MAX);
602 6 : int bad_seq_past = !fd_ulong_is_aligned( seq_past, FD_VINYL_BSTREAM_BLOCK_SZ );
603 6 : int bad_seq_present = !fd_ulong_is_aligned( seq_present, FD_VINYL_BSTREAM_BLOCK_SZ );
604 6 : int bad_info_sz = (info_sz > FD_VINYL_BSTREAM_SYNC_INFO_MAX);
605 6 : int bad_past_order = fd_vinyl_seq_gt( seq_past, seq_present );
606 6 : int bad_past_sz = ((seq_present-seq_past) > mm->dev_sz);
607 :
608 6 : if( FD_UNLIKELY( bad_type | bad_version | bad_val_max | bad_seq_past | bad_seq_present | bad_info_sz |
609 6 : bad_past_order | bad_past_sz ) ) {
610 3 : FD_LOG_WARNING(( "bad sync block when recovering (%s)",
611 3 : bad_type ? "unexpected type" :
612 3 : bad_version ? "unexpected version" :
613 3 : bad_val_max ? "unexpected max pair value decoded byte size" :
614 3 : bad_seq_past ? "unaligned seq_past" :
615 3 : bad_seq_present ? "unaligned seq_present" :
616 3 : bad_info_sz ? "unexpected info size" :
617 3 : bad_past_order ? "unordered seq_past and seq_present" :
618 3 : "past size larger than bstream store" ));
619 3 : return NULL;
620 3 : }
621 :
622 3 : 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 3 : mm->base->seed = io_seed;
628 3 : mm->base->seq_ancient = seq_past;
629 3 : mm->base->seq_past = seq_past;
630 3 : mm->base->seq_present = seq_present;
631 3 : mm->base->seq_future = seq_present;
632 :
633 3 : }
634 :
635 6 : FD_LOG_INFO(( "IO config"
636 6 : "\n\ttype mm"
637 6 : "\n\tspad_max %lu bytes"
638 6 : "\n\tdev_sz %lu bytes"
639 6 : "\n\treset %i"
640 6 : "\n\tinfo \"%s\" (info_sz %lu%s)"
641 6 : "\n\tio_seed 0x%016lx%s",
642 6 : spad_max, dev_sz, reset,
643 6 : info ? (char const *)info : "", info_sz, reset ? "" : ", discovered",
644 6 : io_seed, reset ? "" : " (discovered)" ));
645 :
646 6 : return mm->base;
647 9 : }
648 :
649 : void *
650 6 : fd_vinyl_mmio( fd_vinyl_io_t * io ) {
651 6 : if( FD_UNLIKELY( io->type!=FD_VINYL_IO_TYPE_MM ) ) return NULL;
652 3 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io;
653 3 : return mm->dev + mm->dev_base;
654 6 : }
655 :
656 : ulong
657 6 : fd_vinyl_mmio_sz( fd_vinyl_io_t * io ) {
658 6 : if( FD_UNLIKELY( io->type!=FD_VINYL_IO_TYPE_MM ) ) return 0UL;
659 3 : fd_vinyl_io_mm_t * mm = (fd_vinyl_io_mm_t *)io;
660 3 : return mm->dev_sz;
661 6 : }
|