LCOV - code coverage report
Current view: top level - discof/restore/utils - fd_vinyl_io_wd.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 322 0.0 %
Date: 2026-01-23 05:02:40 Functions: 0 21 0.0 %

          Line data    Source code
       1             : #include "fd_vinyl_io_wd.h"
       2             : #include "fd_ssctrl.h"
       3             : 
       4             : /* fd_vinyl_io_wd manages a pool of DMA-friendly buffers.
       5             : 
       6             :    The system keeps an invariant that
       7             :    - There is a fixed amount of buffers in total.  Buffers transition
       8             :      atomically between either of 3 states:
       9             :    - IDLE:   queue of multiple buffers waiting to move to APPEND
      10             :    - APPEND: zero or one buffer currently being filled
      11             :    - IOWAIT: queue of multiple filled buffers sitting in a write queue
      12             :              waiting for snapwr to report them as complete
      13             : 
      14             : ***********************************************************************/
      15             : 
      16             : /* wd_block_used returns the number of bytes already committed in an
      17             :    APPEND buffer. */
      18             : 
      19             : static ulong
      20           0 : wd_block_used( fd_vinyl_io_wd_t * wd ) {
      21           0 :   if( FD_UNLIKELY( !wd->buf_append ) ) return 0UL;
      22           0 :   wd_buf_t * buf = wd->buf_append;
      23           0 :   if( buf->state!=WD_BUF_APPEND ) FD_LOG_NOTICE(( "append buf state %u", buf->state ));
      24           0 :   FD_CRIT( buf->state==WD_BUF_APPEND, "append buf in invalid state" );
      25             : 
      26           0 :   FD_CRIT( fd_vinyl_seq_ge( wd->base->seq_future, wd->base->seq_present ), "corrupt bstream io state" );
      27           0 :   ulong block_sz = wd->base->seq_future - buf->bstream_seq;
      28           0 :   FD_CRIT( block_sz<=wd->wr_mtu, "corrupt bstream io state" );
      29           0 :   return block_sz;
      30           0 : }
      31             : 
      32             : /* wd_dispatch enqueues the current APPEND buffer for writing, moving it
      33             :    to IOWAIT. */
      34             : 
      35             : static void
      36           0 : wd_dispatch( fd_vinyl_io_wd_t * wd ) {
      37             : 
      38             :   /* Map the current buffer to a bstream sequence range */
      39             : 
      40           0 :   FD_CRIT( wd->buf_append, "no append buf to dispatch" );
      41           0 :   wd_buf_t * buf = wd->buf_append;
      42           0 :   FD_CRIT( buf->state==WD_BUF_APPEND, "append buf in invalid state" );
      43             : 
      44           0 :   FD_CRIT( fd_vinyl_seq_ge( wd->base->seq_future, wd->base->seq_present ), "corrupt bstream io state" );
      45           0 :   ulong block_sz = wd_block_used( wd );
      46           0 :   FD_CRIT( fd_ulong_is_aligned( block_sz, FD_VINYL_BSTREAM_BLOCK_SZ ), "misaligned block_sz (bad appends?)" );
      47           0 :   FD_CRIT( block_sz>0UL, "attempted to dispatch empty buf" );
      48             : 
      49             :   /* Align up block_sz to multiple of 4096 bytes.
      50             :      This step is critical for good O_DIRECT performance. */
      51             : 
      52           0 :   ulong aligned_sz = fd_ulong_align_up( block_sz, 4096UL );
      53           0 :   ulong pad_sz     = aligned_sz - block_sz;
      54           0 :   FD_CRIT( aligned_sz<=wd->wr_mtu, "corrupt bstream io state" );
      55           0 :   if( FD_UNLIKELY( pad_sz ) ) {
      56           0 :     FD_CRIT( fd_ulong_is_aligned( pad_sz, FD_VINYL_BSTREAM_BLOCK_SZ ), "misaligned zero padding computed" );
      57           0 :     for( ulong pad_rem=pad_sz; pad_rem>0UL; pad_rem-=FD_VINYL_BSTREAM_BLOCK_SZ ) {
      58           0 :       memset( buf->buf + block_sz, 0, FD_VINYL_BSTREAM_BLOCK_SZ );
      59           0 :       block_sz             += FD_VINYL_BSTREAM_BLOCK_SZ;
      60           0 :       wd->base->seq_future += FD_VINYL_BSTREAM_BLOCK_SZ;
      61           0 :     }
      62           0 :   }
      63             : 
      64             :   /* Map the bstream sequence range to the device */
      65             : 
      66           0 :   FD_CRIT( buf->bstream_seq+block_sz==wd->base->seq_future, "corrupt bstream io state" );
      67           0 :   if( FD_UNLIKELY( wd->base->seq_future > wd->dev_sz ) ) {
      68             :     /* FIXME log vinyl instance name */
      69           0 :     FD_LOG_ERR(( "vinyl database is out of space (dev_sz=%lu)", wd->dev_sz ));
      70           0 :   }
      71           0 :   ulong dev_off = wd->dev_base + buf->bstream_seq;
      72             : 
      73           0 :   ulong seq   = wd->wr_seq;
      74           0 :   ulong sig   = dev_off;
      75           0 :   ulong ctl   = FD_SNAPSHOT_MSG_DATA;
      76           0 :   ulong chunk = fd_laddr_to_chunk( wd->wr_base, buf->buf );
      77           0 :   ulong sz    = block_sz>>FD_VINYL_BSTREAM_BLOCK_LG_SZ;
      78           0 :   FD_CRIT( sz<=USHORT_MAX, "block_sz too large" );
      79           0 :   fd_mcache_publish( wd->wr_mcache, wd->wr_depth, seq, sig, chunk, sz, ctl, 0UL, 0UL );
      80           0 :   wd->wr_seq = fd_seq_inc( seq, 1UL );
      81             : 
      82           0 :   buf->next           = NULL;
      83           0 :   buf->state          = WD_BUF_IOWAIT;
      84           0 :   buf->io_seq         = seq;
      85           0 :   buf->bstream_seq    = wd->base->seq_future;
      86           0 :   wd->buf_append      = NULL;
      87           0 :   if( wd->buf_iowait_tail  ) wd->buf_iowait_tail->next = buf;
      88           0 :   if( !wd->buf_iowait_head ) wd->buf_iowait_head       = buf;
      89           0 :   wd->buf_iowait_tail = buf;
      90           0 : }
      91             : 
      92             : static void
      93             : fd_vinyl_io_wd_read_imm( fd_vinyl_io_t * io,
      94             :                          ulong           seq0,
      95             :                          void *          _dst,
      96           0 :                          ulong           sz ) {
      97           0 :   (void)io; (void)seq0; (void)_dst; (void)sz;
      98           0 :   FD_LOG_CRIT(( "vinyl_io_wd does not support read_imm" ));
      99           0 : }
     100             : 
     101             : static void
     102             : fd_vinyl_io_wd_read( fd_vinyl_io_t *    io,
     103           0 :                      fd_vinyl_io_rd_t * _rd ) {
     104           0 :   (void)io; (void)_rd;
     105           0 :   FD_LOG_CRIT(( "vinyl_io_wd does not support read" ));
     106           0 : }
     107             : 
     108             : static int
     109             : fd_vinyl_io_wd_poll( fd_vinyl_io_t *     io,
     110             :                      fd_vinyl_io_rd_t ** _rd,
     111           0 :                      int                 flags ) {
     112           0 :   (void)io; (void)_rd; (void)flags;
     113           0 :   FD_LOG_CRIT(( "vinyl_io_wd does not support poll" ));
     114           0 : }
     115             : 
     116             : /* fd_vinyl_io_wd_append starts an asynchronous append operation.  _src
     117             :    is assumed to be a pointer returned by the most recent alloc.
     118             :    Updates io->base->seq_future. */
     119             : 
     120             : static ulong
     121             : fd_vinyl_io_wd_append( fd_vinyl_io_t * io,
     122             :                        void const *    _src,
     123           0 :                        ulong           sz ) {
     124           0 :   fd_vinyl_io_wd_t * wd  = (fd_vinyl_io_wd_t *)io; /* Note: io must be non-NULL to have even been called */
     125           0 :   uchar const *      src = (uchar const *)_src;
     126             : 
     127             :   /* Validate the input args. */
     128             : 
     129           0 :   ulong seq_future = wd->base->seq_future;
     130           0 :   if( FD_UNLIKELY( !sz ) ) return seq_future;
     131             : 
     132           0 :   wd_buf_t * buf = wd->buf_append;
     133           0 :   FD_CRIT( buf, "no append buf" );
     134           0 :   FD_CRIT( buf->state==WD_BUF_APPEND, "corrupt wd_buf" );
     135           0 :   FD_CRIT( src>=buf->buf && (src+sz)<=buf->buf+wd->wr_mtu, "alloc invalidated" );
     136             : 
     137           0 :   int bad_src   = !src || src<wd->wr_chunk0 || (src+sz)>wd->wr_chunk1;
     138           0 :   int bad_align = !fd_ulong_is_aligned( (ulong)src, FD_VINYL_BSTREAM_BLOCK_SZ );
     139           0 :   int bad_sz    = !fd_ulong_is_aligned( sz,         FD_VINYL_BSTREAM_BLOCK_SZ );
     140             : 
     141           0 :   if( FD_UNLIKELY( bad_src | bad_align | bad_sz ) ) {
     142           0 :     if( bad_src   ) FD_LOG_CRIT(( "src not a valid alloc ptr ([%p,%p) not in [%p,%p))",
     143           0 :                                   (void *)src,           (void *)(src+sz),
     144           0 :                                   (void *)wd->wr_chunk0, (void *)wd->wr_chunk1 ));
     145           0 :     if( bad_align ) FD_LOG_CRIT(( "misaligned src %p", (void *)src ));
     146           0 :     if( bad_sz    ) FD_LOG_CRIT(( "misaligned sz %lu", sz ));
     147           0 :   }
     148             : 
     149             :   /* At this point, we appear to have a valid append request.  Map it to
     150             :      the bstream (updating seq_future). */
     151             : 
     152           0 :   ulong seq = seq_future;
     153           0 :   wd->base->seq_future = seq + sz;
     154             : 
     155           0 :   return seq;
     156           0 : }
     157             : 
     158             : /* wd_poll_write checks for write completions (fseq).  Returns
     159             :    FD_VINYL_SUCCESS if all writes completed, FD_VINYL_ERR_AGAIN
     160             :    otherwise.  Updates io->base->seq_present. */
     161             : 
     162             : static int
     163           0 : wd_poll_write( fd_vinyl_io_t * io ) {
     164           0 :   fd_vinyl_io_wd_t * wd = (fd_vinyl_io_wd_t *)io; /* Note: io must be non-NULL to have even been called */
     165             : 
     166           0 :   while( wd->buf_iowait_head ) {
     167           0 :     wd_buf_t * buf = wd->buf_iowait_head;
     168           0 :     FD_CRIT( buf->state==WD_BUF_IOWAIT, "corrupt wd_buf" );
     169             : 
     170           0 :     ulong comp_seq  = buf->io_seq;
     171           0 :     ulong min_fseq  = ULONG_MAX;
     172           0 :     for( ulong i=0; i<wd->wr_fseq_cnt; i++ ) {
     173           0 :       ulong wr_fseq = fd_fseq_query( wd->wr_fseq[ i ] );
     174           0 :       min_fseq      = fd_ulong_min( min_fseq, wr_fseq );
     175           0 :     }
     176           0 :     ulong found_seq = min_fseq;
     177             :     /* each seq in [0,found_seq) is consumed (non-inclusive) */
     178           0 :     if( fd_seq_ge( comp_seq, found_seq ) ) break;
     179           0 :     FD_CRIT( fd_seq_le( found_seq, wd->wr_seq ), "got completion for a sequence number not yet submitted" );
     180           0 :     FD_CRIT( fd_vinyl_seq_eq( comp_seq, wd->wr_seqack ), "out-of-order ACK polling" );
     181             : 
     182             :     /* Buffer completed, remove from IOWAIT list */
     183           0 :     wd->buf_iowait_head = buf->next;
     184           0 :     if( !wd->buf_iowait_head ) wd->buf_iowait_tail = NULL;
     185             : 
     186             :     /* Update seq_present (tracks last write) */
     187           0 :     wd->base->seq_present = fd_ulong_max( wd->base->seq_present, buf->bstream_seq );
     188             : 
     189             :     /* Return buffer to IDLE list */
     190           0 :     buf->state       = WD_BUF_IDLE;
     191           0 :     buf->next        = wd->buf_idle;
     192           0 :     buf->io_seq      = 0UL;
     193           0 :     buf->bstream_seq = 0UL;
     194           0 :     wd->buf_idle     = buf;
     195           0 :     wd->wr_seqack    = fd_seq_inc( comp_seq, 1UL );
     196           0 :   }
     197             : 
     198           0 :   return wd->buf_iowait_head ? FD_VINYL_ERR_AGAIN : FD_VINYL_SUCCESS;
     199           0 : }
     200             : 
     201             : /* fd_vinyl_io_wd_commit completes the dispatches the current APPEND
     202             :    block and polls for completions. */
     203             : 
     204             : static int
     205             : fd_vinyl_io_wd_commit( fd_vinyl_io_t * io,
     206           0 :                        int             flags ) {
     207           0 :   fd_vinyl_io_wd_t * wd = (fd_vinyl_io_wd_t *)io; /* Note: io must be non-NULL to have even been called */
     208             : 
     209           0 :   if( FD_UNLIKELY( fd_vinyl_seq_ne( wd->base->seq_present, wd->base->seq_future ) ) ) {
     210           0 :     FD_CRIT( wd->buf_append, "no append buf to commit, but seq_future indicates inflight write" );
     211             : 
     212           0 :     wd_dispatch( wd );
     213             : 
     214           0 :     wd->base->seq_present = wd->base->seq_future;
     215           0 :     wd->base->spad_used   = 0UL;
     216           0 :   }
     217             : 
     218           0 :   int poll_err;
     219           0 :   do {
     220           0 :     poll_err = wd_poll_write( io );
     221           0 :     if( FD_LIKELY( poll_err!=FD_VINYL_ERR_AGAIN ) ) break;
     222           0 :     FD_SPIN_PAUSE();
     223           0 :   } while( flags & FD_VINYL_IO_FLAG_BLOCKING );
     224           0 :   return poll_err;
     225           0 : }
     226             : 
     227             : static ulong
     228             : fd_vinyl_io_wd_hint( fd_vinyl_io_t * io,
     229           0 :                      ulong           sz ) {
     230           0 :   (void)io; (void)sz;
     231           0 :   FD_LOG_CRIT(( "vinyl_io_wd does not support hint" ));
     232           0 : }
     233             : 
     234             : /* fd_vinyl_io_wd_alloc reserves sz bytes from the current APPEND block
     235             :    (starting a new one or dispatching the previous one if needed).
     236             : 
     237             :    Note that the caller could do two allocs back-to-back, in which case
     238             :    the first alloc should be discarded and replaced by the first.  Also
     239             :    note that trim could reduce the size of an alloc. */
     240             : 
     241             : static void *
     242             : fd_vinyl_io_wd_alloc1( fd_vinyl_io_t * io,
     243           0 :                        ulong           sz ) {
     244           0 :   fd_vinyl_io_wd_t * wd = (fd_vinyl_io_wd_t *)io; /* Note: io must be non-NULL to have even been called */
     245             : 
     246           0 :   if( FD_UNLIKELY( sz > wd->wr_mtu ) ) {
     247           0 :     FD_LOG_CRIT(( "requested write sz (%lu) exceeds MTU (%lu)", sz, wd->wr_mtu ));
     248           0 :   }
     249             : 
     250             :   /* Acquire an append buffer */
     251             : 
     252           0 :   wd_buf_t * buf      = NULL;
     253           0 :   ulong      buf_used = 0UL;
     254           0 :   if( wd->buf_append ) {
     255           0 :     /* */ buf_used = wd_block_used( wd );
     256           0 :     ulong buf_free = wd->wr_mtu - buf_used;
     257           0 :     FD_CRIT( buf_used <= wd->wr_mtu, "corrupt wd_buf" );
     258           0 :     if( FD_LIKELY( sz <= buf_free ) ) {
     259           0 :       buf = wd->buf_append;
     260           0 :     } else {
     261           0 :       wd_dispatch( wd );  /* transition buf from APPEND to IOWAIT */
     262           0 :       buf      = NULL;
     263           0 :       buf_used = 0UL;
     264           0 :     }
     265           0 :   }
     266           0 :   if( FD_UNLIKELY( !buf ) ) {
     267           0 :     if( FD_LIKELY( wd->buf_idle ) ) {
     268             :       /* Start a new buffer */
     269           0 :       FD_CRIT( wd->buf_idle->state==WD_BUF_IDLE, "corrupt wd_buf" );
     270           0 :       wd->buf_append = wd->buf_idle;
     271           0 :       wd->buf_idle   = wd->buf_idle->next;
     272           0 :       buf            = wd->buf_append;
     273             : 
     274           0 :       wd->buf_append->next        = NULL;
     275           0 :       wd->buf_append->state       = WD_BUF_APPEND;
     276           0 :       wd->buf_append->bstream_seq = wd->base->seq_future;
     277           0 :     } else {
     278             :       /* All buffers are IOWAIT, cannot make progress */
     279           0 :       return NULL;
     280           0 :     }
     281           0 :   }
     282             : 
     283             :   /* At this point, we have an APPEND state buffer that is large enough
     284             :      to fit the alloc. */
     285             : 
     286           0 :   FD_CRIT( buf->state==WD_BUF_APPEND, "corrupt wd_buf" );
     287           0 :   return buf->buf + buf_used;
     288           0 : }
     289             : 
     290             : void *
     291             : fd_vinyl_io_wd_alloc( fd_vinyl_io_t * io,
     292             :                       ulong           sz,
     293           0 :                       int             flags ) {
     294           0 :   if( FD_UNLIKELY( !sz ) ) return NULL;
     295           0 :   for(;;) {
     296           0 :     void * p = fd_vinyl_io_wd_alloc1( io, sz );
     297           0 :     if( FD_LIKELY( p || !(flags & FD_VINYL_IO_FLAG_BLOCKING) ) ) {
     298           0 :       return p;
     299           0 :     }
     300           0 :     wd_poll_write( io );
     301           0 :     FD_SPIN_PAUSE();
     302           0 :   }
     303           0 : }
     304             : 
     305             : static ulong
     306             : fd_vinyl_io_wd_copy( fd_vinyl_io_t * io,
     307             :                      ulong           seq_src0,
     308           0 :                      ulong           sz ) {
     309           0 :   (void)io; (void)seq_src0; (void)sz;
     310           0 :   FD_LOG_CRIT(( "vinyl_io_wd does not support copy" ));
     311           0 : }
     312             : 
     313             : static void
     314             : fd_vinyl_io_wd_forget( fd_vinyl_io_t * io,
     315           0 :                        ulong           seq ) {
     316           0 :   (void)io; (void)seq;
     317           0 :   FD_LOG_CRIT(( "vinyl_io_wd does not support forget" ));
     318           0 : }
     319             : 
     320             : static void
     321             : fd_vinyl_io_wd_rewind( fd_vinyl_io_t * io,
     322           0 :                        ulong           seq ) {
     323           0 :   (void)io; (void)seq;
     324           0 :   FD_LOG_CRIT(( "vinyl_io_wd does not support rewind" ));
     325           0 : }
     326             : 
     327             : static int
     328             : fd_vinyl_io_wd_sync( fd_vinyl_io_t * io,
     329           0 :                      int             flags ) {
     330           0 :   (void)io; (void)flags;
     331           0 :   FD_LOG_CRIT(( "vinyl_io_wd does not support sync" ));
     332           0 : }
     333             : 
     334             : static void *
     335           0 : fd_vinyl_io_wd_fini( fd_vinyl_io_t * io ) {
     336           0 :   fd_vinyl_io_wd_t * wd = (fd_vinyl_io_wd_t *)io; /* Note: io must be non-NULL to have even been called */
     337             : 
     338           0 :   ulong seq_present = wd->base->seq_present;
     339           0 :   ulong seq_future  = wd->base->seq_future;
     340             : 
     341           0 :   if( FD_UNLIKELY( fd_vinyl_seq_ne( seq_present, seq_future ) ) ) FD_LOG_WARNING(( "fini discarding uncommited blocks" ));
     342             : 
     343           0 :   return io;
     344           0 : }
     345             : 
     346             : fd_vinyl_io_impl_t fd_vinyl_io_wd_impl = {
     347             :   fd_vinyl_io_wd_read_imm,
     348             :   fd_vinyl_io_wd_read,
     349             :   fd_vinyl_io_wd_poll,
     350             :   fd_vinyl_io_wd_append,
     351             :   fd_vinyl_io_wd_commit,
     352             :   fd_vinyl_io_wd_hint,
     353             :   fd_vinyl_io_wd_alloc,
     354             :   fd_vinyl_io_wd_copy,
     355             :   fd_vinyl_io_wd_forget,
     356             :   fd_vinyl_io_wd_rewind,
     357             :   fd_vinyl_io_wd_sync,
     358             :   fd_vinyl_io_wd_fini
     359             : };
     360             : 
     361             : ulong
     362           0 : fd_vinyl_io_wd_align( void ) {
     363           0 :   return alignof(fd_vinyl_io_wd_t);
     364           0 : }
     365             : 
     366             : ulong
     367           0 : fd_vinyl_io_wd_footprint( ulong queue_depth ) {
     368           0 :   if( FD_UNLIKELY( !queue_depth || !fd_ulong_is_pow2( queue_depth ) || queue_depth>UINT_MAX ) )
     369           0 :     return 0UL;
     370           0 :   return sizeof(fd_vinyl_io_wd_t) + (queue_depth*sizeof(wd_buf_t));
     371           0 : }
     372             : 
     373             : fd_vinyl_io_t *
     374             : fd_vinyl_io_wd_init( void *           lmem,
     375             :                      ulong            dev_sz,
     376             :                      ulong            io_seed,
     377             :                      fd_frag_meta_t * block_mcache,
     378             :                      uchar *          block_dcache,
     379             :                      ulong const **   block_fseq,
     380             :                      ulong            block_fseq_cnt,
     381           0 :                      ulong            block_mtu ) {
     382             :   /* Mostly copied from fd_vinyl_io_bd.c */
     383             : 
     384           0 :   fd_vinyl_io_wd_t * wd = (fd_vinyl_io_wd_t *)lmem;
     385             : 
     386           0 :   if( FD_UNLIKELY( !wd ) ) {
     387           0 :     FD_LOG_WARNING(( "NULL mem" ));
     388           0 :     return NULL;
     389           0 :   }
     390             : 
     391           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)wd, fd_vinyl_io_wd_align() ) ) ) {
     392           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     393           0 :     return NULL;
     394           0 :   }
     395             : 
     396           0 :   if( FD_UNLIKELY( !block_mcache ) ) {
     397           0 :     FD_LOG_WARNING(( "NULL block_mcache" ));
     398           0 :     return NULL;
     399           0 :   }
     400             : 
     401           0 :   if( FD_UNLIKELY( !block_dcache ) ) {
     402           0 :     FD_LOG_WARNING(( "NULL block_dcache" ));
     403           0 :     return NULL;
     404           0 :   }
     405             : 
     406           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)block_dcache, FD_VINYL_BSTREAM_BLOCK_SZ ) ) ) {
     407           0 :     FD_LOG_WARNING(( "misaligned block_dcache (addr=%p)", (void *)block_dcache ));
     408           0 :     return NULL;
     409           0 :   }
     410             : 
     411           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( block_mtu, FD_VINYL_BSTREAM_BLOCK_SZ ) ) ) {
     412           0 :     FD_LOG_WARNING(( "misaligned block_mtu (%lu)", block_mtu ));
     413           0 :     return NULL;
     414           0 :   }
     415             : 
     416           0 :   if( FD_UNLIKELY( block_mtu > (USHORT_MAX<<FD_VINYL_BSTREAM_BLOCK_LG_SZ) ) ) {
     417           0 :     FD_LOG_WARNING(( "oversz block_mtu (%lu)", block_mtu ));
     418           0 :     return NULL;
     419           0 :   }
     420             : 
     421           0 :   ulong block_depth = fd_mcache_depth( block_mcache );
     422           0 :   if( FD_UNLIKELY( !block_depth || !fd_ulong_is_pow2( block_depth ) || block_depth>UINT_MAX ) ) {
     423           0 :     FD_LOG_WARNING(( "block_mcache depth (%lu) invalid", block_depth ));
     424           0 :     return NULL;
     425           0 :   }
     426             : 
     427           0 :   ulong dcache_data_sz = fd_dcache_data_sz( block_dcache );
     428           0 :   if( FD_UNLIKELY( dcache_data_sz<(block_depth*block_mtu) ) ) {
     429           0 :     FD_LOG_WARNING(( "block_dcache size (%lu) too small for block_depth (%lu) and block_mtu (%lu), required at least %lu bytes",
     430           0 :                      dcache_data_sz, block_depth, block_mtu, block_depth*block_mtu ));
     431           0 :     return NULL;
     432           0 :   }
     433             : 
     434           0 :   ulong footprint = fd_vinyl_io_wd_footprint( block_depth );
     435           0 :   if( FD_UNLIKELY( !footprint ) ) {
     436           0 :     FD_LOG_WARNING(( "bad spad_max" ));
     437           0 :     return NULL;
     438           0 :   }
     439             : 
     440           0 :   memset( wd, 0, footprint );
     441             : 
     442           0 :   wd->base->type = FD_VINYL_IO_TYPE_BD;
     443             : 
     444             :   /* Base class members.  Note that vinyl_io does not have an actual
     445             :      scratch pad (emplaces writes directly into dcache).  Instead,
     446             :      spad_{used_max} track the space available in the current APPEND
     447             :      block. */
     448             : 
     449           0 :   wd->base->spad_max  = block_mtu;
     450           0 :   wd->base->spad_used = 0UL;
     451           0 :   wd->base->impl      = &fd_vinyl_io_wd_impl;
     452             : 
     453           0 :   wd->dev_base = FD_VINYL_BSTREAM_BLOCK_SZ;
     454           0 :   wd->dev_sz   = dev_sz - FD_VINYL_BSTREAM_BLOCK_SZ;
     455             : 
     456           0 :   wd->buf_idle        = NULL;
     457           0 :   wd->buf_append      = NULL;
     458           0 :   wd->buf_iowait_head = NULL;
     459           0 :   wd->buf_iowait_tail = NULL;
     460             : 
     461           0 :   wd->wr_mcache = block_mcache;
     462           0 :   wd->wr_seq    = fd_mcache_seq0( block_mcache );
     463           0 :   wd->wr_seqack = wd->wr_seq;
     464           0 :   wd->wr_depth  = block_depth;
     465           0 :   wd->wr_base   = block_dcache;
     466           0 :   wd->wr_chunk0 = wd->wr_base;
     467           0 :   wd->wr_chunk1 = wd->wr_base + (block_depth*block_mtu);
     468           0 :   FD_TEST( block_fseq_cnt<=WD_WR_FSEQ_CNT_MAX );
     469           0 :   for( ulong i=0UL; i<block_fseq_cnt; i++ ) {
     470           0 :     wd->wr_fseq[i] = block_fseq[i];
     471           0 :   }
     472           0 :   wd->wr_fseq_cnt = block_fseq_cnt;
     473           0 :   wd->wr_mtu    = block_mtu;
     474             : 
     475             :   /* Set vinyl io_seed */
     476             : 
     477           0 :   FD_CRIT( fd_dcache_app_sz( block_dcache )>=sizeof(ulong), "block_dcache app region too small to hold io_seed" );
     478           0 :   FD_COMPILER_MFENCE();
     479           0 :   FD_STORE( ulong, fd_dcache_app_laddr( block_dcache ), io_seed );
     480           0 :   FD_COMPILER_MFENCE();
     481             : 
     482             :   /* Set up initial buffer list state */
     483             : 
     484           0 :   wd_buf_t * buf_pool = (wd_buf_t *)(wd+1);
     485           0 :   for( long i=(long)block_depth-1L; i>=0L; i-- ) {
     486           0 :     uchar * block = wd->wr_base + (ulong)i*block_mtu;
     487           0 :     FD_CRIT( fd_ulong_is_aligned( (ulong)block, FD_VINYL_BSTREAM_BLOCK_SZ ), "misaligned wd_buf" );
     488           0 :     wd_buf_t * buf = buf_pool+i;
     489           0 :     *buf = (wd_buf_t) {
     490           0 :       .buf   = block,
     491           0 :       .next  = wd->buf_idle,
     492           0 :       .state = WD_BUF_IDLE
     493           0 :     };
     494           0 :     wd->buf_idle = buf;
     495           0 :   }
     496             : 
     497             :   /* We are starting a new bstream. */
     498             : 
     499           0 :   wd->base->seed        = io_seed;
     500           0 :   wd->base->seq_ancient = 0UL;
     501           0 :   wd->base->seq_past    = 0UL;
     502           0 :   wd->base->seq_present = 0UL;
     503           0 :   wd->base->seq_future  = 0UL;
     504             : 
     505           0 :   FD_LOG_INFO(( "IO config"
     506           0 :                 "\n\ttype        wd"
     507           0 :                 "\n\tblock_depth %lu"
     508           0 :                 "\n\tblock_mtu   %lu bytes"
     509           0 :                 "\n\treset       1"
     510           0 :                 "\n\tio_seed     0x%016lx",
     511           0 :                 block_depth, block_mtu,
     512           0 :                 io_seed ));
     513             : 
     514           0 :   return wd->base;
     515           0 : }
     516             : 
     517             : int
     518           0 : fd_vinyl_io_wd_busy( fd_vinyl_io_t * io ) {
     519           0 :   fd_vinyl_io_wd_t * wd = (fd_vinyl_io_wd_t *)io;
     520           0 :   return !!wd->buf_append || !!wd->buf_iowait_head;
     521           0 : }
     522             : 
     523             : void
     524             : fd_vinyl_io_wd_ctrl( fd_vinyl_io_t * io,
     525             :                      ulong           ctl,
     526           0 :                      ulong           sig ) {
     527           0 :   fd_vinyl_io_wd_t * wd = (fd_vinyl_io_wd_t *)io;
     528             : 
     529           0 :   if( FD_UNLIKELY( fd_vinyl_io_wd_busy( io ) ) ) {
     530           0 :     FD_LOG_CRIT(( "Attempted to send control message via io_wd while still busy" ));
     531           0 :   }
     532             : 
     533           0 :   fd_mcache_publish( wd->wr_mcache, wd->wr_depth, wd->wr_seq, sig, 0UL, 0UL, ctl, 0UL, 0UL );
     534           0 : }

Generated by: LCOV version 1.14