LCOV - code coverage report
Current view: top level - vinyl/cq - fd_vinyl_cq.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 45 58 77.6 %
Date: 2025-12-07 04:58:33 Functions: 7 175 4.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_vinyl_cq_fd_vinyl_cq_h
       2             : #define HEADER_fd_src_vinyl_cq_fd_vinyl_cq_h
       3             : 
       4             : /* A fd_vinyl_comp_t provides details about vinyl request completions. */
       5             : 
       6             : #include "../fd_vinyl_base.h"
       7             : 
       8             : /* FD_VINYL_COMP_{ALIGN,FOOTPRINT} give the byte alignment and footprint
       9             :    of a fd_vinyl_comp_t.  ALIGN is a reasonable power-of-2.  FOOTPRINT
      10             :    is a multiple of ALIGN. */
      11             : 
      12             : #define FD_VINYL_COMP_ALIGN     (32UL)
      13             : #define FD_VINYL_COMP_FOOTPRINT (32UL)
      14             : 
      15             : /* FD_VINYL_COMP_QUOTA_MAX gives the maximum client acquire quota that
      16             :    can be returned by a completion. */
      17             : 
      18             : #define FD_VINYL_COMP_QUOTA_MAX (65535UL)
      19             : 
      20             : /* FIXME: consider eching back the val_gaddr and err_gaddr array back
      21             :    too?  Maybe helpful in the case of read pipelining (e.g. request made
      22             :    on one thread, completion received on a different thread).  But the
      23             :    req_id can probably encode this for a case like this without needing
      24             :    to bloat the completion footprint. */
      25             : 
      26             : struct __attribute__((aligned(FD_VINYL_COMP_ALIGN))) fd_vinyl_comp {
      27             :   ulong  seq;       /* Completion sequence number */
      28             :   ulong  req_id;    /* Echoed from corresponding request */
      29             :   ulong  link_id;   /* Echoed from corresponding request */
      30             :   short  err;       /* FD_VINYL_SUCCESS (0) if the request was processed (see request err array for individual failure details)
      31             :                        FD_VINYL_ERR_* (negative) otherwise (no items in the request were processed) */
      32             :   ushort batch_cnt; /* Num items requested */
      33             :   ushort fail_cnt;  /* If a successful completion, num items that failed processing, in [0,batch_cnt].
      34             :                        If a failed completion, 0 (no items were processed). */
      35             :   ushort quota_rem; /* Client quota remaining when request completed processing.
      36             :                        0<=quota_rem<=client quota_max<=FD_VINYL_COMP_QUOTA_MAX */
      37             : };
      38             : 
      39             : typedef struct fd_vinyl_comp fd_vinyl_comp_t;
      40             : 
      41             : /* A fd_vinyl_cq_t is an interprocess sharable persistent SPMC
      42             :    queue used to communicate fd_vinyl_comp_t completions from a vinyl
      43             :    tile to clients.  It is implemented as a hybrid direct mapped cache /
      44             :    queue with similar lockfree properties to a tango mcache.
      45             :    Specifically, when publishing a completion into a cq, a producer:
      46             : 
      47             :    - Assigns the completion the cq's next sequence number and determines
      48             :      the corresponding cache line.
      49             : 
      50             :    - Sets the cache line seq to seq-1.  Because the cq's cache has at
      51             :      least 4 lines (2 is enough for this particular case), this is a seq
      52             :      that will never be held in that line.  This atomically indicates to
      53             :      a concurrent consumer that seq-comp_cnt is no longer available in
      54             :      cache and that seq is in the process of being published into the
      55             :      line.
      56             : 
      57             :    - Set the rest of the completion fields.
      58             : 
      59             :    - Sets the cache line seq to seq.  This atomically indicates that
      60             :      completion seq is ready.
      61             : 
      62             :    - Advances the cq's seq cursor.  From a consumer's point of view,
      63             :      this cursor has the property that that [0,seq) have been published
      64             :      and (seq,ULONG_MAX] have not been published.  This cursor should
      65             :      only be used by consumers to synchronize their local cursors at
      66             :      initialization.
      67             : 
      68             :    When reading completions from a cq, a consumer:
      69             : 
      70             :    - Determine the cache line for consumer's seq.
      71             : 
      72             :    - Reads the cache line seq.
      73             : 
      74             :    - Reads the rest of the completion fields.
      75             : 
      76             :    - Reads the cache line seq again.
      77             : 
      78             :    If the first and second reads do not match, the vinyl tile is in the
      79             :    process of updating that cache line (most likely the consumer is
      80             :    caught up and producer is about to produce seq ... the consumer
      81             :    should try again soon).
      82             : 
      83             :    Otherwise, if the read sequence numbers match the consumer's seq, the
      84             :    consumer received completion seq and should advance their local seq.
      85             : 
      86             :    If they are behind the consumer's seq, the producer has not written
      87             :    seq yet and the consumer should try again later.
      88             : 
      89             :    If they are ahead of the consumer's seq, the producer has overrun the
      90             :    consumer (the amount ahead gives a ballpark how far the consumer fell
      91             :    behind) and the consumer should recover / reinit.  As such, flow
      92             :    control is managed by the application (e.g. if the application
      93             :    ensures that there are at most comp_cnt completion generating vinyl
      94             :    requests pending at any given time on this cq, no overruns are
      95             :    possible).
      96             : 
      97             :    Note that because fd_vinyl_comp_t are AVX-2 friendly, it is possible
      98             :    to SIMD accelerate producers and consumers (also similar to
      99             :    fd_tango). */
     100             : 
     101             : /* FIXME: consider making comp_cnt a compile time constant? */
     102             : 
     103           3 : #define FD_VINYL_CQ_MAGIC (0xfd3a7352dc03a6c0UL) /* fd warm snd cq magc version 0 */
     104             : 
     105             : struct __attribute__((aligned(128))) fd_vinyl_cq_private {
     106             : 
     107             :   ulong magic;    /* ==FD_VINYL_CQ_MAGIC */
     108             :   ulong comp_cnt; /* Number of completions that can be in flight on this cq at any given time, power of 2 of least 4 */
     109             :   uchar _[ 112 ]; /* Put seq on a separate cache line pair */
     110             :   ulong seq;      /* Completion sequence number to publish next */
     111             :   /* padding to 128 alignment */
     112             :   /* fd_vinyl_comp_t comp[ comp_cnt ] here, seq number at idx = seq & (comp_cnt-1UL) when available */
     113             :   /* padding to 128 alignment */
     114             : 
     115             : };
     116             : 
     117             : typedef struct fd_vinyl_cq_private fd_vinyl_cq_t;
     118             : 
     119             : FD_PROTOTYPES_BEGIN
     120             : 
     121             : /* fd_vinyl_cq_{align,footprint,new,join,leave_delete} have the usual
     122             :    interprocess shared persistent memory object semantics.  comp_cnt is
     123             :    a power-of-2 of at least 4 that gives the number completions that can
     124             :    be in flight on this cq. */
     125             : 
     126             : FD_FN_CONST ulong fd_vinyl_cq_align    ( void );
     127             : FD_FN_CONST ulong fd_vinyl_cq_footprint( ulong comp_cnt );
     128             : void *            fd_vinyl_cq_new      ( void * shmem, ulong comp_cnt );
     129             : fd_vinyl_cq_t *   fd_vinyl_cq_join     ( void * shcq );
     130             : void *            fd_vinyl_cq_leave    ( fd_vinyl_cq_t * cq );
     131             : void *            fd_vinyl_cq_delete   ( void * shcq );
     132             : 
     133             : /* fd_vinyl_cq_comp returns the location in the caller's address space
     134             :    of the cq's completion array.  fd_vinyl_cq_comp_const is a const
     135             :    correct version.  fd_vinyl_cq_comp_cnt is the size of this array.
     136             :    The lifetime of the returned array is the lifetime of the local join.
     137             :    These assume cq is a current local join.
     138             : 
     139             :    fd_vinyl_cq_comp_idx gives the array index that will cache completion
     140             :    seq in a cq completion array with comp_cnt elements. */
     141             : 
     142             : FD_FN_CONST static inline fd_vinyl_comp_t *
     143    75068386 : fd_vinyl_cq_comp( fd_vinyl_cq_t * cq ) {
     144    75068386 :   return (fd_vinyl_comp_t *)(cq+1);
     145    75068386 : }
     146             : 
     147             : FD_FN_CONST static inline fd_vinyl_comp_t const *
     148           3 : fd_vinyl_cq_comp_const( fd_vinyl_cq_t const * cq ) {
     149           3 :   return (fd_vinyl_comp_t const *)(cq+1);
     150           3 : }
     151             : 
     152           3 : FD_FN_PURE static inline ulong fd_vinyl_cq_comp_cnt( fd_vinyl_cq_t const * cq ) { return cq->comp_cnt; }
     153             : 
     154    75098380 : FD_FN_CONST static inline ulong fd_vinyl_cq_comp_idx( ulong seq, ulong comp_cnt ) { return seq & (comp_cnt-1UL); }
     155             : 
     156             : /* fd_vinyl_cq_seq returns the position of the producer's sequence
     157             :    number cursor.  Specifically, at some point during the call,
     158             :    completions [0,seq) were published, completions (seq,ULONG_MAX] were
     159             :    not been published, and completion seq was either published, being
     160             :    published or not published.  This is used for initial synchronization
     161             :    between producer and consumers.  This is a compiler fence. */
     162             : 
     163             : static inline ulong
     164           3 : fd_vinyl_cq_seq( fd_vinyl_cq_t const * cq ) {
     165           3 :   FD_COMPILER_MFENCE();
     166           3 :   ulong seq = cq->seq;
     167           3 :   FD_COMPILER_MFENCE();
     168           3 :   return seq;
     169           3 : }
     170             : 
     171             : /* fd_vinyl_cq_send sends a completion.  If comp is non-NULL, the
     172             :    completion will be written out-of-band to the location comp (assumes
     173             :    comp->seq is not 1 on entry and will set to 1 once the send it done).
     174             :    Otherwise, if cq is non-NULL, the completion will be enquened into
     175             :    the given cq (assumes cq is a current local join).  If both cq and
     176             :    comp are NULL, this is a no-op.  This is a compiler fence. */
     177             : 
     178             : /* FIXME: consider SIMD accelerating */
     179             : 
     180             : static inline void
     181             : fd_vinyl_cq_send( fd_vinyl_cq_t *   cq,
     182             :                   fd_vinyl_comp_t * comp,
     183             :                   ulong             req_id,
     184             :                   ulong             link_id,
     185             :                   int               err,          /* In [-2^15,2^15) */
     186             :                   ulong             batch_cnt,    /* In [0,2^16)  */
     187             :                   ulong             fail_cnt,     /* In [0,2^16) */
     188    75068389 :                   ulong             quota_rem ) { /* In [0,2^16) */
     189             : 
     190    75068389 :   ulong stack_seq[1];
     191             : 
     192    75068389 :   ulong   seq;
     193    75068389 :   ulong * _seq;
     194             : 
     195    75068389 :   if( FD_UNLIKELY( comp ) ) { /* Send directly */
     196             : 
     197           6 :     seq  = 1UL;       /* For direct sends, comp->seq should have already been set to something != 1 */
     198           6 :     _seq = stack_seq;
     199             : 
     200    75068383 :   } else if( FD_LIKELY( cq ) ) { /* Send via cq */
     201             : 
     202    75068380 :     seq  = cq->seq;
     203    75068380 :     _seq = &cq->seq;
     204    75068380 :     comp = fd_vinyl_cq_comp( cq ) + fd_vinyl_cq_comp_idx( seq, cq->comp_cnt );
     205             : 
     206    75068380 :     FD_COMPILER_MFENCE();
     207    75068380 :     comp->seq = seq - 1UL; /* Mark completion seq being written */
     208             : 
     209    75068380 :   } else { /* No place to send completion */
     210             : 
     211           3 :     FD_COMPILER_MFENCE(); /* Consistent semantics in all cases */
     212           3 :     return;
     213             : 
     214           3 :   }
     215             : 
     216    75068386 :   FD_COMPILER_MFENCE();
     217    75068386 :   comp->req_id    = req_id;
     218    75068386 :   comp->link_id   = link_id;
     219    75068386 :   comp->err       = (short)err;
     220    75068386 :   comp->batch_cnt = (ushort)batch_cnt;
     221    75068386 :   comp->fail_cnt  = (ushort)fail_cnt;
     222    75068386 :   comp->quota_rem = (ushort)quota_rem;
     223    75068386 :   FD_COMPILER_MFENCE();
     224    75068386 :   comp->seq = seq; /* Mark completion seq as written */
     225    75068386 :   FD_COMPILER_MFENCE();
     226    75068386 :   *_seq = seq + 1UL; /* Record that completions [0,seq) are published */
     227    75068386 :   FD_COMPILER_MFENCE();
     228             : 
     229    75068386 : }
     230             : 
     231             : /* fd_vinyl_cq_recv receives completion seq from the given cq.  Returns
     232             :    0 on success, positive if completion seq has not been published yet
     233             :    and negative if the consumer has been overrun by the producer.  On
     234             :    return, *dst contains the desired completion on success and is
     235             :    clobbered otherwise.  Assumes cq is a current local join and dst is
     236             :    valid.  This is a compiler fence. */
     237             : 
     238             : /* FIXME: consider SIMD accelerating */
     239             : 
     240             : static inline long
     241             : fd_vinyl_cq_recv( fd_vinyl_cq_t const * cq,
     242             :                   ulong                 seq,
     243           0 :                   fd_vinyl_comp_t *     dst ) {
     244           0 :   fd_vinyl_comp_t const * src = fd_vinyl_cq_comp_const( cq ) + fd_vinyl_cq_comp_idx( seq, cq->comp_cnt );
     245             : 
     246           0 :   FD_COMPILER_MFENCE();
     247           0 :   ulong seq0 = src->seq;
     248           0 :   FD_COMPILER_MFENCE();
     249           0 :   *dst = *src;
     250           0 :   FD_COMPILER_MFENCE();
     251           0 :   ulong seq1 = src->seq;
     252           0 :   FD_COMPILER_MFENCE();
     253             : 
     254           0 :   long diff0 = (long)(seq-seq0);
     255           0 :   long diff1 = (long)(seq-seq1);
     256           0 :   return fd_long_if( !diff0, diff1, diff0 );
     257           0 : }
     258             : 
     259             : FD_PROTOTYPES_END
     260             : 
     261             : #endif /* HEADER_fd_src_vinyl_cq_fd_vinyl_cq_h */

Generated by: LCOV version 1.14