LCOV - code coverage report
Current view: top level - disco/quic - fd_tpu.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 43 51 84.3 %
Date: 2026-07-12 05:24:35 Functions: 11 63 17.5 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_disco_quic_fd_tpu_h
       2             : #define HEADER_fd_src_disco_quic_fd_tpu_h
       3             : 
       4             : /* fd_tpu provides the server-side of the TPU/QUIC protocol.
       5             : 
       6             :    TPU/QUIC is the protocol used to submit transactions to a block
       7             :    producer.  For each txn to be transferred, the client opens a
       8             :    unidirectional QUIC stream and sends its serialization (see
       9             :    fd_txn_parse).  In the happy case, a txn only requires one packet.
      10             : 
      11             :    For txn exceeding MTU size, the txn is fragmented over multiple
      12             :    packets.  For more information, see the specification:
      13             :    https://github.com/solana-foundation/specs/blob/main/p2p/tpu.md */
      14             : 
      15             : #include "../fd_txn_m.h"
      16             : #include "../../tango/fd_tango_base.h"
      17             : 
      18             : /* fd_tpu_msg_t is the payload of a quic_verify frag. */
      19             : 
      20             : struct __attribute__((aligned(FD_CHUNK_ALIGN))) fd_tpu_msg {
      21             :   fd_txn_m_t hdr;
      22             :   uchar      payload[ FD_TPU_MTU ];
      23             :   /* No txn_t nor alut info follows */
      24             : };
      25             : 
      26             : typedef struct fd_tpu_msg fd_tpu_msg_t;
      27             : 
      28             : /* FD_TPU_REASM_{SUCCESS,ERR_{...}} are error codes.  These values are
      29             :    persisted to logs.  Entries should not be renumbered and numeric
      30             :    values should never be reused. */
      31             : 
      32      131682 : #define FD_TPU_REASM_SUCCESS   (0)
      33           0 : #define FD_TPU_REASM_ERR_SZ    (1)  /* oversz msg */
      34           0 : #define FD_TPU_REASM_ERR_SKIP  (2)  /* out-of-order data within QUIC stream */
      35           0 : #define FD_TPU_REASM_ERR_STATE (3)  /* unexpected slot state */
      36             : 
      37             : /* FD_TPU_REASM_STATE_{...} are reasm slot states */
      38             : 
      39    19469967 : #define FD_TPU_REASM_STATE_FREE ((uchar)0)  /* free */
      40    19470351 : #define FD_TPU_REASM_STATE_BUSY ((uchar)1)  /* active reassembly */
      41    19535493 : #define FD_TPU_REASM_STATE_PUB  ((uchar)2)  /* published */
      42             : 
      43             : /* fd_tpu_reasm_t handles incoming data fragments of TPU/QUIC streams.
      44             :    Frags are expected to be provided via fd_quic callback.  Each
      45             :    tpu_reasm object may only serve a single fd_quic object.  Dispatches
      46             :    reassembled messages to an mcache.)  Should not be persisted.
      47             : 
      48             :    ### Flow Control
      49             : 
      50             :    fd_tpu_reasm is wired up as follows:
      51             : 
      52             :      ┌────────┐           ┌───────┐       ┌────────┐
      53             :      │  QUIC  │ callbacks │ tpu_  │ tango │ sig_   │
      54             :      │ Server ├───────────► reasm ├───────► verify │
      55             :      └────────┘           └───────┘       └────────┘
      56             : 
      57             :    Neither of the pictured links backpressure.  Packet loss occurs if
      58             :    (1) the QUIC server accepts more concurrent streams than available
      59             :    reassembly slots.  Also if (2) the bank of sig verify tiles is too
      60             :    slow to keepup with incoming transactions.
      61             : 
      62             :    The application should thus adjust the QUIC server to throttle the
      63             :    concurrent stream count and transaction rate to appropriate levels.
      64             :    (Via QUIC connection quotas)
      65             : 
      66             :    The tpu_reasm MUST be the only writer to the mcache.
      67             : 
      68             :    ### Eviction Policy
      69             : 
      70             :    Aforementioned case 1 specifically happens whenever the QUIC server
      71             :    accepts a stream and tpu_reasm doesn't find a free slot.  tpu_reasm
      72             :    hardcodes a FIFO eviction policy to handle this case by cancelling
      73             :    the least recently prepared reassembly.  This also guarantees that
      74             :    unfragmented transaction never get dropped.
      75             : 
      76             :    ### Internals
      77             : 
      78             :    fd_tpu_reasm internally manages an array of message reassembly
      79             :    buffers.  Each of these is called a "slot" (fd_tpu_reasm_slot_t).
      80             : 
      81             :    Slots are either owned by the reassembly fifo (FREE, BUSY states), or
      82             :    the mcache (PUB state).  The ownership separation prevents in-flight
      83             :    reassemblies from thrashing data exposed to consumers via the mcache.
      84             :    (Data races transitioning between reassembly and fifo ownership are
      85             :    handled by the speculative receive pattern.)
      86             : 
      87             :    The lifecycle of a slot is:
      88             : 
      89             :             prepare()  publish()
      90             :      ┌─► FREE ───► BUSY ───► PUB ─┐
      91             :      │              │             │
      92             :      ▲              ▼ cancel()    ▼ implied by a later
      93             :      │              │             │ publish()/cancel()
      94             :      └──────◄───────┴──────◄──────┘
      95             : 
      96             :    prepare: The transition from FREE to BUSY occurs when a new QUIC
      97             :             stream is accepted.
      98             :    cancel:  The transition from BUSY to FREE occurs when stream/txn
      99             :             reassembly is aborted.  This can happen for whatever
     100             :             explicit reason (peer kicked, network error), or implicitly
     101             :             when prepare() is called but no free slot was found.
     102             :    publish: The transition from BUSY to PUB occurs when a slot holding
     103             :             a complete txn is made visible to downstream consumers.
     104             :             This moves a slot from the reassembly fifo to the mcache.
     105             : 
     106             :    The transition from PUB to FREE also occurs at the same time (for a
     107             :    different slot).  This moves the least recently published slot from
     108             :    the mcache into the reassembly fifo.  This keeps the number of slots
     109             :    owned by the mcache at _exactly_ depth at all times and exactly
     110             :    mirroring the set of packets exposed downstream (notwithstanding a
     111             :    startup transient of up to depth packets).  This also guarantees that
     112             :    the number of slots in the FREE and BUSY states is kept at _exactly_
     113             :    reasm_max at all times.
     114             : 
     115             :    In order to support the above, the 'pub_slots' lookup table tracks
     116             :    which published mcache lines (indexed by `seq % depth`) correspond to
     117             :    which slot indexes. */
     118             : 
     119             : 
     120             : /* fd_tpu_reasm_slot_t holds a message reassembly buffer.
     121             :    Carefully tuned to 32 byte size. */
     122             : 
     123             : struct fd_tpu_reasm_key {
     124             :   ulong conn_uid; /* ULONG_MAX means invalid */
     125             :   ulong stream_id : 48;
     126             :   ulong sz        : 14; /* size of the txn payload data.  does not
     127             :                            include the sizeof(fd_txn_m_t) bytes that
     128             :                            precedes the payload in each slot. */
     129             :   ulong state     : 2;
     130             : };
     131             : 
     132    19547799 : #define FD_TPU_REASM_SID_MASK (0xffffffffffffUL)
     133       65841 : #define FD_TPU_REASM_SZ_MASK  (0x3fffUL)
     134             : 
     135             : typedef struct fd_tpu_reasm_key fd_tpu_reasm_key_t;
     136             : 
     137             : struct __attribute__((aligned(16))) fd_tpu_reasm_slot {
     138             :   fd_tpu_reasm_key_t k; /* FIXME ugly: the compound key has to be a single struct member */
     139             :   uint lru_prev;
     140             :   uint lru_next;
     141             :   uint chain_next;
     142             :   uint tsorig_comp;
     143             : };
     144             : 
     145             : typedef struct fd_tpu_reasm_slot fd_tpu_reasm_slot_t;
     146             : 
     147             : struct fd_tpu_reasm {
     148             :   ulong magic;  /* ==FD_TPU_REASM_MAGIC */
     149             : 
     150             :   ulong slots_off;     /* slots mem     */
     151             :   ulong pub_slots_off; /* pub_slots mem */
     152             :   ulong map_off;       /* map mem */
     153             : 
     154             :   fd_tpu_msg_t * dcache;
     155             : 
     156             :   uint   depth;       /* mcache depth */
     157             :   uint   burst;       /* max concurrent reassemblies */
     158             : 
     159             :   uint   head;        /* least recent reassembly */
     160             :   uint   tail;        /* most  recent reassembly */
     161             : 
     162             :   uint   slot_cnt;
     163             :   ushort orig;        /* tango orig */
     164             : };
     165             : 
     166             : typedef struct fd_tpu_reasm fd_tpu_reasm_t;
     167             : 
     168             : FD_PROTOTYPES_BEGIN
     169             : 
     170             : /* Private accessors */
     171             : 
     172             : static inline FD_FN_PURE fd_tpu_reasm_slot_t *
     173    20227863 : fd_tpu_reasm_slots_laddr( fd_tpu_reasm_t * reasm ) {
     174    20227863 :   return (fd_tpu_reasm_slot_t *)( (ulong)reasm + reasm->slots_off );
     175    20227863 : }
     176             : 
     177             : static inline FD_FN_PURE fd_tpu_reasm_slot_t const *
     178      358953 : fd_tpu_reasm_slots_laddr_const( fd_tpu_reasm_t const * reasm ) {
     179      358953 :   return (fd_tpu_reasm_slot_t const *)( (ulong)reasm + reasm->slots_off );
     180      358953 : }
     181             : 
     182             : static inline FD_FN_PURE uint *
     183      216774 : fd_tpu_reasm_pub_slots_laddr( fd_tpu_reasm_t * reasm ) {
     184      216774 :   return (uint *)( (ulong)reasm + reasm->pub_slots_off );
     185      216774 : }
     186             : 
     187             : /* Construction API */
     188             : 
     189             : /* fd_tpu_reasm_{align,footprint} return the required alignment and
     190             :    footprint of a memory region suitable for use as a tpu_reasm that
     191             :    can reassemble up to 'reasm_max' txns concurrently.  'depth' is the
     192             :    entry count of the target mcache.  mtu is the max sz of a serialized
     193             :    txn (usually FD_TXN_MTU). */
     194             : 
     195             : FD_FN_CONST ulong
     196             : fd_tpu_reasm_align( void );
     197             : 
     198             : FD_FN_CONST ulong
     199             : fd_tpu_reasm_footprint( ulong depth,       /* Assumed in {2^0,2^1,2^2,...,2^31} */
     200             :                         ulong reasm_max ); /* Assumed in [1,2^31) */
     201             : 
     202             : FD_FN_CONST static inline ulong
     203             : fd_tpu_reasm_req_data_sz( ulong depth,
     204           9 :                           ulong reasm_max ) { /* Assumed in [1,2^31) */
     205           9 :   return (depth+reasm_max) * sizeof(fd_tpu_msg_t);
     206           9 : }
     207             : 
     208             : /* fd_tpu_reasm_new formats an unused memory region for use as a
     209             :    tpu_reasm.  shmem is a non-NULL pointer to this region in the local
     210             :    address space with the required footprint and alignment.  {depth,
     211             :    reasm_max,mtu} as described above.  orig is the Tango origin ID of
     212             :    this tpu_reasm.  dcache is a local join to an fd_dcache that
     213             :    tpu_reasm will write frags to.  dcache should have at least
     214             :    fd_tpu_reasm_req_data_sz() bytes of data_sz.  The dcache app region
     215             :    is ignored and not written to. */
     216             : 
     217             : void *
     218             : fd_tpu_reasm_new( void * shmem,
     219             :                   ulong  depth,     /* Assumed in {2^0,2^1,2^2,...,2^32} */
     220             :                   ulong  reasm_max, /* Assumed in [1,2^32) */
     221             :                   ulong  orig,      /* Assumed in [0,FD_FRAG_META_ORIG_MAX) */
     222             :                   void * dcache );
     223             : 
     224             : fd_tpu_reasm_t *
     225             : fd_tpu_reasm_join( void * shreasm );
     226             : 
     227             : void *
     228             : fd_tpu_reasm_leave( fd_tpu_reasm_t * reasm );
     229             : 
     230             : void *
     231             : fd_tpu_reasm_delete( void * shreasm );
     232             : 
     233             : /* Accessor API */
     234             : 
     235             : fd_tpu_reasm_slot_t *
     236             : fd_tpu_reasm_query( fd_tpu_reasm_t * reasm,
     237             :                     ulong            conn_uid,
     238             :                     ulong            stream_id );
     239             : 
     240             : FD_FN_PURE static inline fd_tpu_reasm_slot_t *
     241           0 : fd_tpu_reasm_peek_tail( fd_tpu_reasm_t * reasm ) {
     242           0 :   uint                  tail_idx = reasm->tail;
     243           0 :   fd_tpu_reasm_slot_t * tail     = fd_tpu_reasm_slots_laddr( reasm ) + tail_idx;
     244           0 :   return tail;
     245           0 : }
     246             : 
     247             : fd_tpu_reasm_slot_t *
     248             : fd_tpu_reasm_prepare( fd_tpu_reasm_t * reasm,
     249             :                       ulong            conn_uid,
     250             :                       ulong            stream_id,
     251             :                       long             tspub );
     252             : 
     253             : static inline fd_tpu_reasm_slot_t *
     254             : fd_tpu_reasm_acquire( fd_tpu_reasm_t * reasm,
     255             :                       ulong            conn_uid,
     256             :                       ulong            stream_id,
     257       76509 :                       long             tspub ) {
     258       76509 :   fd_tpu_reasm_slot_t * slot = fd_tpu_reasm_query( reasm, conn_uid, stream_id );
     259       76509 :   if( !slot ) {
     260       76125 :     slot = fd_tpu_reasm_prepare( reasm, conn_uid, stream_id, tspub );
     261       76125 :   }
     262       76509 :   return slot;
     263       76509 : }
     264             : 
     265             : /* fd_tpu_reasm_frag appends a new stream frag to the reasm slot.
     266             :    [data,data+data_sz) is the memory region containing the stream data.
     267             :    data_off is the offset of this stream data.  Slot reassembly buffer
     268             :    is appended with copy of [data,data+data_sz) on success.  On failure,
     269             :    cancels the reassembly.
     270             : 
     271             :    Return values one of:
     272             : 
     273             :      FD_TPU_REASM_SUCCESS:   success, fragment added to reassembly
     274             :      FD_TPU_REASM_EAGAIN:    incomplete
     275             :      FD_TPU_REASM_ERR_SZ:    fail, data_off + data_sz  > mtu
     276             :      FD_TPU_REASM_ERR_SKIP:  fail, data_off - slot->sz > 0
     277             : 
     278             :    Note on SKIP error:  RFC 9000 Section 2.2 specifies "QUIC makes no
     279             :    specific allowances for delivery of stream data out of order." */
     280             : 
     281             : int
     282             : fd_tpu_reasm_frag( fd_tpu_reasm_t *      reasm,
     283             :                    fd_tpu_reasm_slot_t * slot,
     284             :                    uchar const *         data,
     285             :                    ulong                 sz,
     286             :                    ulong                 off );
     287             : 
     288             : /* fd_tpu_reasm_publish completes a stream reassembly and publishes the
     289             :    message to an mcache for downstream consumption.  base is the address
     290             :    of the chunk whose index is 0 (chunk0 param of fd_chunk_to_laddr).
     291             :    {seq,sig,tspub} are mcache frag params.  If slot does not have active
     292             :    reassembly or txn parsing failed, returns NULL.  If base is not valid
     293             :    for tpu_reasm, aborts.  Final msg sz in [0,mtu+FD_CHUNK_SZ). */
     294             : 
     295             : int
     296             : fd_tpu_reasm_publish( fd_tpu_reasm_t *      reasm,
     297             :                       fd_tpu_reasm_slot_t * slot,
     298             :                       fd_frag_meta_t *      mcache,
     299             :                       void *                base,  /* Assumed aligned FD_CHUNK_ALIGN */
     300             :                       ulong                 seq,
     301             :                       long                  tspub,
     302             :                       uint                  source_ipv4,
     303             :                       uchar                 source_tpu );
     304             : 
     305             : /* fd_tpu_reasm_publish_fast is a streamlined version of acquire/frag/
     306             :    publish. */
     307             : 
     308             : int
     309             : fd_tpu_reasm_publish_fast( fd_tpu_reasm_t * reasm,
     310             :                            uchar const *    data,
     311             :                            ulong            sz,
     312             :                            fd_frag_meta_t * mcache,
     313             :                            void *           base,  /* Assumed aligned FD_CHUNK_ALIGN */
     314             :                            ulong            seq,
     315             :                            long             tspub,
     316             :                            uint             source_ipv4,
     317             :                            uchar            source_tpu );
     318             : 
     319             : /* fd_tpu_reasm_cancel cancels the given stream reassembly. */
     320             : 
     321             : void
     322             : fd_tpu_reasm_cancel( fd_tpu_reasm_t *      reasm,
     323             :                      fd_tpu_reasm_slot_t * slot );
     324             : 
     325             : /* fd_tpu_reasm_key_hash is an unrolled version of fd_hash (xxhash-r39) */
     326             : 
     327    78228588 : #define C1 (11400714785074694791UL)
     328    58671441 : #define C2 (14029467366897019727UL)
     329    19557147 : #define C3 ( 1609587929392839161UL)
     330    39114294 : #define C4 ( 9650029242287828579UL)
     331    19557147 : #define C5 ( 2870177450012600261UL)
     332             : 
     333             : FD_FN_PURE static inline ulong
     334             : fd_tpu_reasm_key_hash( fd_tpu_reasm_key_t const * k,
     335    19557147 :                        ulong                      seed ) {
     336             : 
     337    19557147 :   ulong h  = seed + C5 + 16UL;
     338    19557147 :   ulong w0 = k->conn_uid;
     339    19557147 :   ulong w1 = k->stream_id;
     340             : 
     341    19557147 :   w0 *= C2; w0 = fd_ulong_rotate_left( w0, 31 ); w0 *= C1; h ^= w0; h = fd_ulong_rotate_left( h, 27 )*C1 + C4;
     342    19557147 :   w1 *= C2; w1 = fd_ulong_rotate_left( w1, 31 ); w1 *= C1; h ^= w1; h = fd_ulong_rotate_left( h, 27 )*C1 + C4;
     343             : 
     344             :   /* Final avalanche */
     345    19557147 :   h ^= h >> 33;
     346    19557147 :   h *= C2;
     347    19557147 :   h ^= h >> 29;
     348    19557147 :   h *= C3;
     349    19557147 :   h ^= h >> 32;
     350             : 
     351    19557147 :   return h;
     352    19557147 : }
     353             : 
     354             : #undef C1
     355             : #undef C2
     356             : #undef C3
     357             : #undef C4
     358             : #undef C5
     359             : 
     360             : FD_PROTOTYPES_END
     361             : 
     362             : #endif /* HEADER_fd_src_disco_quic_fd_tpu_h */

Generated by: LCOV version 1.14