LCOV - code coverage report
Current view: top level - discof/replay - fd_sched.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 6 12 50.0 %
Date: 2026-08-14 04:54:57 Functions: 0 0 -

          Line data    Source code
       1             : #ifndef HEADER_fd_src_discof_replay_fd_sched_h
       2             : #define HEADER_fd_src_discof_replay_fd_sched_h
       3             : 
       4             : #include "fd_rdisp.h"
       5             : #include "../../disco/fd_txn_p.h"
       6             : #include "../../disco/store/fd_store.h" /* for fd_store_fec_t */
       7             : #include "../../flamenco/accdb/fd_accdb.h"
       8             : 
       9             : /* fd_sched wraps all the smarts and mechanical chores around scheduling
      10             :    transactions for replay execution.  It is built on top of the
      11             :    dispatcher fd_rdisp.  The dispatcher is responsible for high
      12             :    performance lane-based scheduling of transactions.  On top of that,
      13             :    we add fork-aware management of lanes, and policies regarding which
      14             :    lanes to prioritize for execution.
      15             : 
      16             :    Conceptually, transactions in a block form a DAG.  We would like to
      17             :    make our way through a block with a sufficient degree of parallelism,
      18             :    such that the execution time of the critical path of the DAG is the
      19             :    limiting factor.  The dispatcher does a good job of emerging the
      20             :    critical path of the DAG on the fly.  Blocks are tracked by the
      21             :    dispatcher either as a block staged on a lane, or as an unstaged
      22             :    block.  When a block is staged, it will enjoy the most intelligent
      23             :    online scheduling that the dispatcher has to offer.  Lanes have to
      24             :    consist of linear chains of blocks down a fork.  So to map a fork
      25             :    tree to lanes, we will need multiple lanes.  Ideally, every branch in
      26             :    the fork tree sits on some lane.  However, memory footprint limits us
      27             :    to a few number of lanes.
      28             : 
      29             :    This module implements a state machine for ensuring that blocks enter
      30             :    into and exit out of lanes in an orderly fashion.  The public APIs of
      31             :    this module are invoked to drive state transitions on a small number
      32             :    of events, such as new transactions arriving, or transactions
      33             :    completing, or a block being aborted/abandoned.  We also implement
      34             :    policies for deciding which blocks get staged onto lanes, or evicted
      35             :    from lanes, as well as which lanes to prioritize for execution.
      36             : 
      37             : 
      38             :    The general order in which calls happen under the normal case is:
      39             : 
      40             :    fd_sched_fec_ingest()* ... fd_sched_txn_next_ready()* ... fd_sched_txn_done()* ...
      41             :    more ingest, more ready, more done ...
      42             :    ...
      43             :    fd_sched_txn_next_ready() indicates that the last transaction in the block is being scheduled
      44             :    fd_sched_txn_done()*
      45             :    fd_sched_block_is_done()
      46             :    end-of-block processing in caller
      47             :    fd_sched_txn_next_ready() starts returning transactions from the next block
      48             :    more ingest, more ready, more done ...
      49             :    ... */
      50             : 
      51          12 : #define FD_SCHED_MIN_DEPTH 478
      52             : #define FD_SCHED_MAX_DEPTH FD_RDISP_MAX_DEPTH
      53             : 
      54             : struct fd_sched;
      55             : typedef struct fd_sched fd_sched_t;
      56             : 
      57             : struct fd_sched_alut_ctx {
      58             :   fd_accdb_t *       accdb;
      59             :   fd_accdb_fork_id_t fork_id;
      60             :   ulong              els; /* Effective lookup slot. */
      61             : };
      62             : typedef struct fd_sched_alut_ctx fd_sched_alut_ctx_t;
      63             : 
      64             : struct fd_sched_fec {
      65             :   ulong            bank_idx;            /* Index of the block.  Assumed to be in [0, block_cnt_max).  Caller
      66             :                                            is responsible for ensuring that bank idx is in bounds and unique
      67             :                                            across equivocated blocks. */
      68             :   ulong            parent_bank_idx;     /* Index of the parent block.  Assumed to be in [0, block_cnt_max).
      69             :                                            Caller is responsible for ensuring that parent bank idx is in
      70             :                                            bounds and unique across equivocated blocks. */
      71             :   ulong            slot;                /* Slot number of the block. */
      72             :   ulong            parent_slot;         /* Slot number of the parent block. */
      73             :   fd_store_fec_t * fec;                 /* FEC set metadata. */
      74             :   uchar          * data;                /* Resolved laddr of the FEC set data buffer. */
      75             :   uint             shred_cnt;           /* Number of shreds in the FEC set. */
      76             :   uint             is_last_in_batch:1;  /* Set if this is the last FEC set in the batch; relevant because the
      77             :                                            parser should ignore trailing bytes at the end of a batch. */
      78             :   uint             is_last_in_block:1;  /* Set if this is the last FEC set in the block. */
      79             :   uint             is_first_in_block:1; /* Set if this is the first FEC set in the block.  Bank should increment refcnt for sched if such a FEC set has been ingested by sched. */
      80             : 
      81             :   fd_sched_alut_ctx_t alut_ctx[ 1 ];
      82             : };
      83             : typedef struct fd_sched_fec fd_sched_fec_t;
      84             : 
      85             : /* The state of a transaction.  Non mutually exclusive. */
      86           0 : #define FD_SCHED_TXN_EXEC_DONE      (0x0001UL)
      87           0 : #define FD_SCHED_TXN_SIGVERIFY_DONE (0x0002UL)
      88           0 : #define FD_SCHED_TXN_IS_COMMITTABLE (0x0004UL)
      89           0 : #define FD_SCHED_TXN_IS_FEES_ONLY   (0x0008UL)
      90             : #define FD_SCHED_TXN_REPLAY_DONE    (FD_SCHED_TXN_EXEC_DONE|FD_SCHED_TXN_SIGVERIFY_DONE)
      91             : 
      92             : struct fd_sched_txn_info {
      93             :    ulong flags;
      94             :    int   txn_err;
      95             :    long  tick_parsed;
      96             :    long  tick_sigverify_disp;
      97             :    long  tick_sigverify_done;
      98             :    long  tick_exec_disp;
      99             :    long  tick_exec_done;
     100             :    ulong index_in_slot; /* 0-indexed position of this transaction within its block. */
     101             : };
     102             : typedef struct fd_sched_txn_info fd_sched_txn_info_t;
     103             : 
     104             : /* The scheduler may return one of the following types of tasks for the
     105             :    replay tile.
     106             : 
     107             :    e - passed down to exec tiles.
     108             :    i - replay completes the task immediately.
     109             :    q - replay may either do it immediately or queue the task up. */
     110          36 : #define FD_SCHED_TT_NULL          (0UL)
     111          72 : #define FD_SCHED_TT_BLOCK_START   (1UL) /* (i) Start-of-block processing. */
     112          24 : #define FD_SCHED_TT_BLOCK_END     (2UL) /* (q) End-of-block processing. */
     113           0 : #define FD_SCHED_TT_TXN_EXEC      (3UL) /* (e) Transaction execution. */
     114           0 : #define FD_SCHED_TT_TXN_SIGVERIFY (4UL) /* (e) Transaction sigverify. */
     115             : #define FD_SCHED_TT_LTHASH        (5UL) /* (e) Account lthash. */
     116          45 : #define FD_SCHED_TT_POH_HASH      (6UL) /* (e) PoH hashing. */
     117           6 : #define FD_SCHED_TT_MARK_DEAD     (7UL) /* (i) Mark the block dead. */
     118             : 
     119             : struct fd_sched_block_start {
     120             :   ulong bank_idx;        /* Same as in fd_sched_fec_t. */
     121             :   ulong parent_bank_idx; /* Same as in fd_sched_fec_t. */
     122             :   ulong slot;            /* Slot number of the block. */
     123             : };
     124             : typedef struct fd_sched_block_start fd_sched_block_start_t;
     125             : 
     126             : struct fd_sched_block_end {
     127             :   ulong bank_idx;
     128             : };
     129             : typedef struct fd_sched_block_end fd_sched_block_end_t;
     130             : 
     131             : struct fd_sched_txn_exec {
     132             :   ulong bank_idx;
     133             :   ulong slot;
     134             :   ulong txn_idx;
     135             :   ulong exec_idx;
     136             : };
     137             : typedef struct fd_sched_txn_exec fd_sched_txn_exec_t;
     138             : 
     139             : struct fd_sched_txn_sigverify {
     140             :   ulong bank_idx;
     141             :   ulong txn_idx;
     142             :   ulong exec_idx;
     143             : };
     144             : typedef struct fd_sched_txn_sigverify fd_sched_txn_sigverify_t;
     145             : 
     146             : struct fd_sched_poh_hash {
     147             :   ulong     bank_idx;
     148             :   ulong     mblk_idx;
     149             :   ulong     exec_idx;
     150             :   ulong     hashcnt;
     151             :   fd_hash_t hash[ 1 ];
     152             : };
     153             : typedef struct fd_sched_poh_hash fd_sched_poh_hash_t;
     154             : 
     155             : struct fd_sched_mark_dead {
     156             :   ulong     bank_idx;
     157             : };
     158             : typedef struct fd_sched_mark_dead fd_sched_mark_dead_t;
     159             : 
     160             : struct fd_sched_task {
     161             :   ulong task_type; /* Set to one of the task types defined above. */
     162             :   union {
     163             :     fd_sched_block_start_t   block_start[ 1 ];
     164             :     fd_sched_block_end_t     block_end[ 1 ];
     165             :     fd_sched_txn_exec_t      txn_exec[ 1 ];
     166             :     fd_sched_txn_sigverify_t txn_sigverify[ 1 ];
     167             :     fd_sched_poh_hash_t      poh_hash[ 1 ];
     168             :     fd_sched_mark_dead_t     mark_dead[ 1 ];
     169             :   };
     170             : };
     171             : typedef struct fd_sched_task fd_sched_task_t;
     172             : 
     173             : struct __attribute__((packed)) fd_microblock_hdr {
     174             :   /* Number of PoH hashes between this and last microblock */
     175             :   /* 0x00 */ ulong hash_cnt;
     176             : 
     177             :   /* PoH state after evaluating this microblock (including all
     178             :      appends and mixin). The input to the poh calculation of the first
     179             :      microblock is the last hash of the parent block, otherwise it is the
     180             :      hash of the previous microblock. */
     181             :   /* 0x08 */ uchar hash[32];
     182             : 
     183             :   /* Number of transactions in this microblock */
     184             :   /* 0x28 */ ulong txn_cnt;
     185             : };
     186             : typedef struct fd_microblock_hdr fd_microblock_hdr_t;
     187             : 
     188             : FD_PROTOTYPES_BEGIN
     189             : 
     190             : /* fd_sched_{align,footprint} return the required alignment and
     191             :    footprint in bytes for a region of memory to be used as a scheduler.
     192             :    footprint silently returns 0 if params are invalid (thus convenient
     193             :    to validate params).
     194             : 
     195             :    depth controls the reorder buffer transaction count (~1 million
     196             :    recommended for live replay, ~10k recommended for async replay).
     197             :    block_cnt_max is the maximum number of blocks that will be tracked by
     198             :    the scheduler. */
     199             : 
     200             : ulong
     201             : fd_sched_align( void );
     202             : 
     203             : ulong
     204             : fd_sched_footprint( ulong depth,           /* in [FD_SCHED_MIN_DEPTH,FD_SCHED_MAX_DEPTH] */
     205             :                     ulong block_cnt_max ); /* >= 1 */
     206             : 
     207             : /* fd_sched_new creates a sched object backed by the given memory region
     208             :    (conforming to align() and footprint()).  Returns NULL if any
     209             :    parameter is invalid. */
     210             : 
     211             : void *
     212             : fd_sched_new( void *     mem,
     213             :               fd_rng_t * rng,
     214             :               ulong      depth,
     215             :               ulong      block_cnt_max,
     216             :               ulong      exec_cnt );
     217             : 
     218             : fd_sched_t *
     219             : fd_sched_join( void * mem );
     220             : 
     221             : /* Add the data in the FEC set to the scheduler.  If is_last_fec is 1,
     222             :    then this is the last FEC set in the block.  Transactions may span
     223             :    FEC set boundaries.  The scheduler is responsible for incrementally
     224             :    parsing transactions from concatenated FEC set data.  Assumes that
     225             :    FEC sets are delivered in replay order.  That is, forks form a
     226             :    partial ordering over FEC sets: in-order per fork, but arbitrary
     227             :    ordering across forks.  The fork tree is implied by the stream of
     228             :    parent-child relationships delivered in FEC sets.  Also assumes that
     229             :    there is enough space in the scheduler to ingest the FEC set.  The
     230             :    caller should generally call fd_sched_fec_can_ingest() first.
     231             : 
     232             :    Returns 1 on success, 0 if the block is bad and should be marked
     233             :    dead. */
     234             : FD_WARN_UNUSED int
     235             : fd_sched_fec_ingest( fd_sched_t * sched, fd_sched_fec_t * fec );
     236             : 
     237             : /* Check if there is enough space in the scheduler to ingest the data in
     238             :    the FEC set.  Returns 1 if there is, 0 otherwise.  This is a cheap
     239             :    and conservative check. */
     240             : int
     241             : fd_sched_fec_can_ingest( fd_sched_t * sched, fd_sched_fec_t * fec );
     242             : 
     243             : /* Returns the number of worst-case FEC sets sched can ingest. This is a
     244             :    cheap and conservative check. */
     245             : ulong
     246             : fd_sched_can_ingest_cnt( fd_sched_t * sched );
     247             : 
     248             : /* Returns 1 if sched is drained, 0 otherwise.  A drained scheduler will
     249             :    not return more work.  Otherwise, next_ready will return more work,
     250             :    so long as there are exec tiles available. */
     251             : int
     252             : fd_sched_is_drained( fd_sched_t * sched );
     253             : 
     254             : /* Obtain a transaction eligible for execution.  This implies that all
     255             :    prior transactions with w-r or w-w conflicts have completed.
     256             :    Information regarding the scheduled transaction is written to the out
     257             :    pointer.  Returns 1 on success, 0 on failure.  Failures are generally
     258             :    transient and non-fatal, and are simply an indication that no
     259             :    transaction is ready for execution yet.  When in-flight transactions
     260             :    retire or when more FEC sets are ingested, more transactions may
     261             :    become ready for execution.
     262             : 
     263             :    Transactions on the same fork will be returned in a way that
     264             :    maintains the serial fiction.  That is, reordering can happen, but
     265             :    only within the constraint that transactions appear to be ready in
     266             :    the order in which they occur in the block.  Transactions from
     267             :    different forks may interleave, and the caller should be prepared to
     268             :    switch execution context in response to interleavings.  The scheduler
     269             :    will barrier on block boundaries, in the sense that transactions from
     270             :    a subsequent block will not be returned for execution until all
     271             :    transactions from the previous block have completed.  This gives the
     272             :    caller a chance to perform end-of-block processing before
     273             :    transactions from a subsequent block start executing.  In general,
     274             :    the caller should check if the last transaction in the current block
     275             :    is done, and if so, do end-of-block processing before calling this
     276             :    function to start the next block.
     277             : 
     278             :    In addition to returning transactions for execution, this function
     279             :    may also return a sigverify task.  Sigverify can be completed
     280             :    asynchronously outside the critical path of transaction execution, as
     281             :    long as every transaction in a block passes sigverify before we
     282             :    commit the block.  The scheduler prioritizes actual execution of
     283             :    transactions over sigverify, and in general sigverify tasks are only
     284             :    returned when no real transaction can be dispatched.  In other words,
     285             :    the scheduler tries to exploit idle cycles in the exec tiles during
     286             :    times of low parallelism critical path progression.
     287             : 
     288             :    This function may also return a PoH hashing task.  These tasks are
     289             :    lower priority than transaction execution, but higher priority than
     290             :    sigverify.  This is because sigverify tasks are generally bite-sized,
     291             :    whereas PoH hashing can be longer, so we would like to get started on
     292             :    hashing sooner rather than later. */
     293             : ulong
     294             : fd_sched_task_next_ready( fd_sched_t * sched, fd_sched_task_t * out );
     295             : 
     296             : /* Mark a task as complete.  For transaction execution, this means that
     297             :    the effects of the execution are now visible on any core that could
     298             :    execute a subsequent transaction.  Returns 0 on success, -1 if given
     299             :    the result of the task, the block turns out to be bad.  -1 is only
     300             :    returned from PoH tasks.
     301             : 
     302             :    If a block has been abandoned or marked dead for any reason, it'll be
     303             :    pruned the moment in-flight task count hits 0 due to the last task
     304             :    completing.  Then, in the immediate ensuing stem run loop,
     305             :    sched_pruned_next() will return the index for the corresponding bank
     306             :    so the refcnt can be decremented for sched.
     307             : 
     308             :    The transaction at the given index may be freed upon return from this
     309             :    function.  Nonetheless, as long as there is no intervening FEC
     310             :    ingestion, it would still be safe to query the transaction using
     311             :    get_txn(). */
     312             : int
     313             : fd_sched_task_done( fd_sched_t * sched, ulong task_type, ulong txn_idx, ulong exec_idx, void * data );
     314             : 
     315             : /* Abandon a block.  This means that we are no longer interested in
     316             :    executing the block.  This also implies that any block which chains
     317             :    off of the provided block shall be abandoned.  This is mainly used
     318             :    when a block is aborted because we decided that it would be a
     319             :    dead/invalid block, and so there's no point in spending resources
     320             :    executing it.  The scheduler will no longer return transactions from
     321             :    abandoned blocks for execution.  This should only be invoked on an
     322             :    actively replayed block, and should only be invoked once on it.
     323             : 
     324             :    For the purposes of bank lifetime management, sched is a subsidiary
     325             :    of banks.  So while sched sets things in motion for a bad block to be
     326             :    eagerly pruned, banks/replay is the sole initiator of actual pruning.
     327             :    The way this works is that an abandoned block will have its refcnt
     328             :    queued for release by sched as soon as, and only if, the block has no
     329             :    more in-flight tasks associated with it.  No sooner, no later.  In
     330             :    the immediate ensuing stem run loop, sched_pruned_next() will return
     331             :    the index for the corresponding bank so the refcnt can be decremented
     332             :    for sched.  After that point, banks will eventually instruct sched to
     333             :    prune the block, when all other components release their refcnts on
     334             :    said bank.  Then the bank_idx may be recycled for another block. */
     335             : void
     336             : fd_sched_block_abandon( fd_sched_t * sched, ulong bank_idx );
     337             : 
     338             : /* Prune the given block including descendants of it. */
     339             : void
     340             : fd_sched_cancel( fd_sched_t * sched, ulong bank_idx );
     341             : 
     342             : /* Add a block as immediately done to the scheduler.  This is useful for
     343             :    installing the snapshot slot, or for informing the scheduler of a
     344             :    packed leader block.  Parent block should be ULONG_MAX for the
     345             :    snapshot slot, and otherwise a block that hasn't been pruned. */
     346             : void
     347             : fd_sched_block_add_done( fd_sched_t * sched, ulong bank_idx, ulong parent_bank_idx, ulong slot );
     348             : 
     349             : /* Advance the root, pruning all blocks across forks that do not descend
     350             :    from the new root.  Assumes the new root is in the fork tree and
     351             :    connected to the current root.  Also assumes that there are no more
     352             :    in-flight transactions from the soon-to-be-pruned blocks.  This
     353             :    should be called after root_notify() and the caller is responsible
     354             :    for figuring out the new root to safely prune to. */
     355             : void
     356             : fd_sched_advance_root( fd_sched_t * sched, ulong root_idx );
     357             : 
     358             : /* Notify the scheduler of a new root.  This has the effect of calling
     359             :    abandon() on all minority forks that do not descend from the new
     360             :    root.  Shortly after a call to this function, in-flight transactions
     361             :    from these abandoned blocks should retire from the execution
     362             :    pipeline, and the new root will be safe for pruning. */
     363             : void
     364             : fd_sched_root_notify( fd_sched_t * sched, ulong root_idx );
     365             : 
     366             : /* Returns the index of a bank whose refcnt should be decremented for
     367             :    sched.  This function should be called in a loop to drain all
     368             :    outstanding refcnt decrements before any other sched API is called in
     369             :    a stem run loop.  Returns ULONG_MAX when there are no more
     370             :    outstanding references from sched and the loop should break. */
     371             : ulong
     372             : fd_sched_pruned_block_next( fd_sched_t * sched );
     373             : 
     374             : void
     375             : fd_sched_set_poh_params( fd_sched_t * sched, ulong bank_idx, ulong tick_height, ulong max_tick_height, ulong hashes_per_tick, fd_hash_t const * start_poh );
     376             : 
     377             : /* fd_sched_block_verify_ticks sets the tick window and verifies
     378             :    ticks on bank_idx (shred fuzz harness, no exec); 0 if valid. */
     379             : int
     380             : fd_sched_block_verify_ticks( fd_sched_t * sched,
     381             :                              ulong        bank_idx,
     382             :                              ulong        tick_height,
     383             :                              ulong        max_tick_height,
     384             :                              ulong        hashes_per_tick );
     385             : 
     386             : /* fd_sched_set_bypass_poh_verify configures whether the per-microblock
     387             :    PoH end_hash comparison in maybe_mixin is bypassed.  This is intended
     388             :    for test and fuzz harnesses: the expected end_hash is carried in the
     389             :    shred payload, so comparing it would reject any mutated input before
     390             :    the deeper parse/tick logic is exercised.  Production call sites
     391             :    should leave this disabled. */
     392             : void
     393             : fd_sched_set_bypass_poh_verify( fd_sched_t * sched, int bypass_poh_verify );
     394             : 
     395             : /* fd_sched_set_bypass_alut_resolution bypasses ALUT resolution during
     396             :    parsing (test/fuzz: no accounts DB).  ALUT txns become serializing.
     397             :    Production call sites should leave this disabled. */
     398             : void
     399             : fd_sched_set_bypass_alut_resolution( fd_sched_t * sched, int bypass_alut_resolution );
     400             : 
     401             : fd_txn_p_t *
     402             : fd_sched_get_txn( fd_sched_t * sched, ulong txn_idx );
     403             : 
     404             : fd_sched_txn_info_t *
     405             : fd_sched_get_txn_info( fd_sched_t * sched, ulong txn_idx );
     406             : 
     407             : fd_hash_t *
     408             : fd_sched_get_poh( fd_sched_t * sched, ulong bank_idx );
     409             : 
     410             : uint
     411             : fd_sched_get_shred_cnt( fd_sched_t * sched, ulong bank_idx );
     412             : 
     413             : void
     414             : fd_sched_metrics_write( fd_sched_t * sched );
     415             : 
     416             : /* Serialize the current state as a cstr to the returned buffer.  Caller
     417             :    may read from the buffer until the next invocation of any fd_sched
     418             :    function. */
     419             : char *
     420             : fd_sched_get_state_cstr( fd_sched_t * sched );
     421             : 
     422             : void *
     423             : fd_sched_leave( fd_sched_t * sched );
     424             : 
     425             : void *
     426             : fd_sched_delete( void * mem );
     427             : 
     428             : FD_PROTOTYPES_END
     429             : 
     430             : #endif /* HEADER_fd_src_discof_replay_fd_sched_h */

Generated by: LCOV version 1.14