LCOV - code coverage report
Current view: top level - waltz/quic - fd_quic_ack_tx.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 95 95 100.0 %
Date: 2026-11-12 04:25:03 Functions: 5 5 100.0 %

          Line data    Source code
       1             : #include "fd_quic_ack_tx.h"
       2             : #include "fd_quic_private.h"
       3             : 
       4             : #include "fd_quic_proto.h"
       5             : #include "fd_quic_proto.c"
       6             : 
       7             : static inline int
       8             : fd_quic_range_can_extend( fd_quic_range_t const * range,
       9   140912931 :                           ulong                   idx ) {
      10   140912931 :   return idx+1UL >= range->offset_lo && idx <= range->offset_hi;
      11   140912931 : }
      12             : 
      13             : static inline int
      14             : fd_quic_range_extend( fd_quic_range_t * range,
      15   140588031 :                       ulong             idx ) {
      16   140588031 :   int lo_decreased = idx <  range->offset_lo;
      17   140588031 :   int hi_increased = idx >= range->offset_hi;
      18   140588031 :   range->offset_lo = fd_ulong_min( range->offset_lo, idx );
      19   140588031 :   range->offset_hi = fd_ulong_max( range->offset_hi, idx+1UL );
      20   140588031 :   return lo_decreased || hi_increased;
      21   140588031 : }
      22             : 
      23             : int
      24             : fd_quic_ack_pkt( fd_quic_ack_gen_t * gen,
      25             :                  ulong               pkt_number,
      26             :                  uint                enc_level,
      27   140912946 :                  long                now ) {
      28             : 
      29   140912946 :   if( pkt_number == FD_QUIC_PKT_NUM_UNUSED ) return FD_QUIC_ACK_TX_NOOP;
      30             : 
      31             :   /* Can we merge pkt_number into the most recent ACK? */
      32   140912931 :   uint            cached_seq = gen->head - 1U;
      33   140912931 :   fd_quic_ack_t * cached_ack = fd_quic_ack_queue_ele( gen, cached_seq );
      34   140912931 :   if( ( enc_level == cached_ack->enc_level ) &
      35   140912931 :       fd_quic_range_can_extend( &cached_ack->pkt_number, pkt_number ) ) {
      36             : 
      37             :     /* update timestamp */
      38   140588031 :     if( pkt_number >= cached_ack->pkt_number.offset_hi ) {
      39   140588016 :       cached_ack->ts = now;
      40   140588016 :     }
      41             : 
      42             :     /* add packet number to existing range */
      43   140588031 :     int changed = fd_quic_range_extend( &cached_ack->pkt_number, pkt_number );
      44             : 
      45             :     /* re-enqueue most recent ACK for sending */
      46   140588031 :     if( changed && gen->head==gen->tail ) {
      47      303424 :       gen->tail = cached_seq;
      48      303424 :     }
      49             : 
      50   140588031 :     FD_ACK_DEBUG( FD_LOG_DEBUG(( "gen=%p queue ACK for enc=%u pkt_num=%lu range=[%lu,%lu) seq=%u (merged)",
      51   140588031 :         (void *)gen, enc_level, pkt_number, cached_ack->pkt_number.offset_lo, cached_ack->pkt_number.offset_hi, cached_seq )); )
      52   140588031 :     return FD_QUIC_ACK_TX_MERGED;
      53             : 
      54   140588031 :   }
      55             : 
      56             :   /* Attempt to allocate another ACK queue entry */
      57      324900 :   if( gen->head - gen->tail >= FD_QUIC_ACK_QUEUE_CNT ) {
      58           3 :     FD_DEBUG( FD_LOG_DEBUG(( "ACK queue overflow! (excessive reordering)" )); )
      59           3 :     return FD_QUIC_ACK_TX_ENOSPC;
      60           3 :   }
      61             : 
      62             :   /* Start new pending ACK */
      63      324897 :   FD_ACK_DEBUG( FD_LOG_DEBUG(( "gen=%p queue ACK for enc=%u pkt_num=%lu seq=%u",
      64      324897 :     (void *)gen, enc_level, pkt_number, gen->head )); )
      65      324897 :   fd_quic_ack_t * next_ack = fd_quic_ack_queue_ele( gen, gen->head );
      66      324897 :   *next_ack = (fd_quic_ack_t) {
      67      324897 :     .pkt_number = { .offset_lo = pkt_number, .offset_hi = pkt_number+1UL },
      68      324897 :     .enc_level  = (uchar)enc_level,
      69      324897 :     .ts         = now
      70      324897 :   };
      71      324897 :   gen->head += 1U;
      72      324897 :   return FD_QUIC_ACK_TX_NEW;
      73      324900 : }
      74             : 
      75             : void
      76             : fd_quic_ack_gen_abandon_enc_level( fd_quic_ack_gen_t * gen,
      77       24282 :                                    uint                enc_level ) {
      78             :   /* Entries in the ring are not ordered by enc_level, so compact the
      79             :      whole ring instead of only popping from the tail. */
      80       24282 :   uint w = gen->tail;
      81       42522 :   for( uint r = gen->tail; r != gen->head; r++ ) {
      82       18240 :     fd_quic_ack_t const * ack = fd_quic_ack_queue_ele( gen, r );
      83       18240 :     if( ack->enc_level <= enc_level ) {
      84       12156 :       FD_DEBUG( FD_LOG_DEBUG(( "gen=%p discard ACK for enc=%u range=[%lu,%lu) seq=%u",
      85       12156 :           (void *)gen, ack->enc_level, ack->pkt_number.offset_lo, ack->pkt_number.offset_hi, r )); )
      86       12156 :       continue;
      87       12156 :     }
      88        6084 :     if( w != r ) *fd_quic_ack_queue_ele( gen, w ) = *ack;
      89        6084 :     w++;
      90        6084 :   }
      91       24282 :   gen->head = w;
      92       24282 : }
      93             : 
      94             : extern ulong
      95             : fd_quic_encode_ack_frame( uchar *               buf,
      96             :                           ulong                 sz,
      97             :                           fd_quic_ack_frame_t * frame );
      98             : 
      99             : uchar *
     100             : fd_quic_gen_ack_frames( fd_quic_ack_gen_t * gen,
     101             :                         uchar *             payload_ptr,
     102             :                         uchar *             payload_end,
     103             :                         uint                enc_level,
     104             :                         long                now,
     105     9227906 :                         int                 piggyback ) {
     106             : 
     107     9227906 :   FD_ACK_DEBUG( FD_LOG_DEBUG(( "[ACK gen] elicited=%d", gen->is_elicited )); )
     108             :   /* Never send an ACK-only packet unless an ACK-eliciting packet is
     109             :      pending: two such peers would ACK each other's ACKs forever.  ACKs
     110             :      riding in an already ack-eliciting packet are always fine. */
     111     9227906 :   if( !gen->is_elicited && !piggyback ) return payload_ptr;
     112             : 
     113             :   /* Attempt to send all ACK ranges */
     114     9209366 :   ulong ranges_sent = 0UL;
     115     9524925 :   for( ; gen->tail != gen->head; gen->tail++ ) {
     116      321658 :     fd_quic_ack_t * ack = fd_quic_ack_queue_ele( gen, gen->tail );
     117      321658 :     if( ack->enc_level != enc_level ) {
     118        6078 :       FD_ACK_DEBUG( FD_LOG_DEBUG(( "need encryption level %u for ACKs but have %u", ack->enc_level, enc_level )); )
     119        6078 :       break;
     120        6078 :     }
     121             : 
     122      315580 :     long ack_delay_ns = fd_long_sat_sub( now, ack->ts );
     123      315580 :     long ack_delay_us = ack_delay_ns / (long)1e3;
     124             : 
     125      315580 :     if( FD_UNLIKELY( ack->pkt_number.offset_lo == ack->pkt_number.offset_hi ) ) continue;
     126      315580 :     fd_quic_ack_frame_t ack_frame = {
     127      315580 :       .type            = 0x02, /* type 0x02 is the base ack, 0x03 indicates ECN */
     128      315580 :       .largest_ack     = ack->pkt_number.offset_hi - 1U,
     129      315580 :       .ack_delay       = (ulong)ack_delay_us,
     130      315580 :       .ack_range_count = 0, /* no fragments */
     131      315580 :       .first_ack_range = ack->pkt_number.offset_hi - ack->pkt_number.offset_lo - 1U,
     132      315580 :     };
     133      315580 :     ulong frame_sz = fd_quic_encode_ack_frame( payload_ptr, (ulong)( payload_end - payload_ptr ), &ack_frame );
     134      315580 :     if( FD_UNLIKELY( frame_sz==FD_QUIC_ENCODE_FAIL ) ) {
     135          21 :       FD_DEBUG( FD_LOG_DEBUG(( "insufficient buffer space to send ACK" )); )
     136          21 :       break;
     137          21 :     }
     138      315559 :     payload_ptr += frame_sz;
     139      315559 :     ranges_sent += 1UL;
     140      315559 :     FD_ACK_DEBUG( FD_LOG_DEBUG(( "gen=%p sending ACK enc=%u range=[%lu,%lu) seq=%u sz=%lu",
     141      315559 :         (void *)gen, enc_level, ack->pkt_number.offset_lo, ack->pkt_number.offset_hi, gen->tail, frame_sz )); )
     142      315559 :   }
     143             : 
     144             :   /* If all frames were flushed, reset status bits. */
     145     9209366 :   if( gen->head == gen->tail ) {
     146     9203267 :     gen->is_elicited = 0;
     147     9203267 :   } else {
     148        6099 :     FD_ACK_DEBUG( FD_LOG_DEBUG(( "Not all ACK frames were flushed" )); )
     149        6099 :   }
     150             : 
     151     9209366 :   int const flushed = !gen->is_elicited;
     152     9209366 :   FD_DTRACE_PROBE_3( fd_quic_gen_ack_frames, enc_level, ranges_sent, flushed );
     153             : 
     154     9209366 :   return payload_ptr;
     155     9227906 : }

Generated by: LCOV version 1.14