|
| 1 | +/* Delivery Rate Sampling */ |
| 2 | + |
| 3 | +#include <haproxy/pool.h> |
| 4 | +#include <haproxy/quic_cc-t.h> |
| 5 | +#include <haproxy/quic_cc_drs.h> |
| 6 | +#include <haproxy/quic_tx-t.h> |
| 7 | +#include <haproxy/ticks.h> |
| 8 | +#include <haproxy/window_filter.h> |
| 9 | + |
| 10 | +static void quic_cc_rs_init(struct quic_cc_rs *rs) |
| 11 | +{ |
| 12 | + rs->interval = UINT32_MAX; |
| 13 | + rs->delivered = 0; |
| 14 | + rs->prior_delivered = 0; |
| 15 | + rs->prior_time = TICK_ETERNITY; |
| 16 | + rs->tx_in_flight = 0; |
| 17 | + rs->lost = 0; |
| 18 | + rs->prior_lost = 0; |
| 19 | + rs->send_elapsed = 0; |
| 20 | + rs->ack_elapsed = 0; |
| 21 | + rs->last_end_seq = -1; |
| 22 | + rs->is_app_limited = 0; |
| 23 | +} |
| 24 | + |
| 25 | +void quic_cc_drs_init(struct quic_cc_drs *drs) |
| 26 | +{ |
| 27 | + quic_cc_rs_init(&drs->rs); |
| 28 | + wf_init(&drs->wf, 12, 0, ~0U); |
| 29 | + drs->round_count = 0; |
| 30 | + drs->next_round_delivered = 0; |
| 31 | + drs->delivered = 0; |
| 32 | + drs->lost = 0; |
| 33 | + drs->last_seq = -1; |
| 34 | + drs->delivered_time = TICK_ETERNITY; |
| 35 | + drs->first_sent_time = TICK_ETERNITY; |
| 36 | + drs->app_limited = 0; |
| 37 | + drs->is_cwnd_limited = 0; |
| 38 | +} |
| 39 | + |
| 40 | +/* Update <pkt> TX packet rate sampling information. |
| 41 | + * Must be called after <pkt> has just been sent. |
| 42 | + */ |
| 43 | +void quic_cc_drs_on_pkt_sent(struct quic_cc_path *path, |
| 44 | + struct quic_tx_packet *pkt, struct quic_cc_drs *drs) |
| 45 | +{ |
| 46 | + if (!path->in_flight) |
| 47 | + drs->first_sent_time = drs->delivered_time = pkt->time_sent; |
| 48 | + |
| 49 | + pkt->rs.first_sent_time = drs->first_sent_time; |
| 50 | + pkt->rs.delivered_time = drs->delivered_time; |
| 51 | + pkt->rs.delivered = drs->delivered; |
| 52 | + pkt->rs.is_app_limited = drs->app_limited != 0; |
| 53 | + |
| 54 | + pkt->rs.tx_in_flight = path->in_flight + pkt->len; |
| 55 | + pkt->rs.lost = drs->lost; |
| 56 | + pkt->rs.end_seq = ++drs->last_seq; |
| 57 | +} |
| 58 | + |
| 59 | +/* Return 1 if <pkt> TX packet is the most recently sent packet |
| 60 | + * that has been delivered, 0 if not. |
| 61 | + */ |
| 62 | +static inline int quic_cc_drs_is_newest_packet(struct quic_cc_drs *drs, |
| 63 | + struct quic_tx_packet *pkt) |
| 64 | +{ |
| 65 | + return tick_is_lt(drs->first_sent_time, pkt->time_sent) || |
| 66 | + (pkt->time_sent == drs->first_sent_time && |
| 67 | + pkt->rs.end_seq > drs->rs.last_end_seq); |
| 68 | +} |
| 69 | + |
| 70 | +/* RFC https://datatracker.ietf.org/doc/draft-ietf-ccwg-bbr/ |
| 71 | + * 4.5.2.3.3. Upon receiving an ACK |
| 72 | + * |
| 73 | + * When an ACK arrives, the sender invokes GenerateRateSample() to fill |
| 74 | + * in a rate sample. For each packet that was newly SACKed or ACKed, |
| 75 | + * UpdateRateSample() updates the rate sample based on a snapshot of |
| 76 | + * connection delivery information from the time at which the packet was |
| 77 | + * last transmitted. UpdateRateSample() is invoked multiple times when |
| 78 | + * a stretched ACK acknowledges multiple data packets. In this case we |
| 79 | + * use the information from the most recently sent packet, i.e., the |
| 80 | + * packet with the highest "P.delivered" value. |
| 81 | + * |
| 82 | + * haproxy implementation: quic_cc_drs_update_rate_sample() matches with |
| 83 | + * RFC UpdateRateSample() called from first part of GenerateRateSample(). |
| 84 | + */ |
| 85 | +void quic_cc_drs_update_rate_sample(struct quic_cc_drs *drs, |
| 86 | + struct quic_tx_packet *pkt) |
| 87 | +{ |
| 88 | + struct quic_cc_rs *rs = &drs->rs; |
| 89 | + |
| 90 | + if (!tick_isset(pkt->rs.delivered_time)) |
| 91 | + return; |
| 92 | + |
| 93 | + drs->delivered += pkt->len; |
| 94 | + drs->delivered_time = now_ms; |
| 95 | + /* Update info using the newest packet. */ |
| 96 | + if (tick_isset(rs->prior_time) && !quic_cc_drs_is_newest_packet(drs, pkt)) |
| 97 | + return; |
| 98 | + |
| 99 | + rs->prior_delivered = pkt->rs.delivered; |
| 100 | + rs->prior_time = pkt->rs.delivered_time; |
| 101 | + rs->is_app_limited = pkt->rs.is_app_limited; |
| 102 | + rs->send_elapsed = pkt->time_sent - pkt->rs.first_sent_time; |
| 103 | + rs->ack_elapsed = drs->delivered_time - pkt->rs.delivered_time; |
| 104 | + rs->tx_in_flight = pkt->rs.tx_in_flight; |
| 105 | + rs->prior_lost = pkt->rs.lost; |
| 106 | + rs->last_end_seq = pkt->rs.end_seq; |
| 107 | + drs->first_sent_time = pkt->time_sent; |
| 108 | + /* Mark the packet as delivered once it's SACKed to |
| 109 | + * avoid being used again when it's cumulatively acked. |
| 110 | + */ |
| 111 | + pkt->rs.delivered_time = TICK_ETERNITY; |
| 112 | +} |
| 113 | + |
| 114 | +/* RFC https://datatracker.ietf.org/doc/draft-ietf-ccwg-bbr/ |
| 115 | + * 4.5.2.3.3. Upon receiving an ACK |
| 116 | + * |
| 117 | + * haproxy implementation: second part of GenerateRateSample(). Follows the |
| 118 | + * first one above. |
| 119 | + */ |
| 120 | +void quic_cc_drs_on_ack_recv(struct quic_cc_drs *drs, struct quic_cc_path *path, |
| 121 | + uint64_t pkt_delivered) |
| 122 | +{ |
| 123 | + struct quic_cc_rs *rs = &drs->rs; |
| 124 | + uint64_t rate; |
| 125 | + |
| 126 | + if (drs->app_limited && drs->delivered > drs->app_limited) |
| 127 | + drs->app_limited = 0; |
| 128 | + |
| 129 | + if (pkt_delivered >= drs->next_round_delivered) { |
| 130 | + drs->next_round_delivered = pkt_delivered; |
| 131 | + ++drs->round_count; |
| 132 | + } |
| 133 | + |
| 134 | + if (!tick_isset(rs->prior_time)) |
| 135 | + return; |
| 136 | + |
| 137 | + rs->interval = MAX(rs->send_elapsed, rs->ack_elapsed); |
| 138 | + |
| 139 | + BUG_ON(drs->delivered <= rs->prior_delivered); |
| 140 | + rs->delivered = drs->delivered - rs->prior_delivered; |
| 141 | + BUG_ON(drs->lost < rs->prior_lost); |
| 142 | + rs->lost = drs->lost - rs->prior_lost; |
| 143 | + |
| 144 | + if (rs->interval < path->loss.rtt_min) { |
| 145 | + rs->interval = UINT32_MAX; |
| 146 | + return; |
| 147 | + } |
| 148 | + |
| 149 | + if (!rs->interval) |
| 150 | + return; |
| 151 | + |
| 152 | + rate = rs->delivered * 1000 / rs->interval; |
| 153 | + if (rate >= wf_get_max(&drs->wf) || !drs->app_limited) |
| 154 | + path->delivery_rate = wf_max_update(&drs->wf, rate, drs->round_count); |
| 155 | +} |
0 commit comments