lsquic_bw_sampler.c revision 06b2a236
1/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc. See LICENSE. */ 2#include <assert.h> 3#include <inttypes.h> 4#include <stddef.h> 5#include <stdint.h> 6#include <sys/queue.h> 7 8#include "lsquic_int_types.h" 9#include "lsquic_types.h" 10#include "lsquic_hash.h" 11#include "lsquic.h" 12#include "lsquic_conn.h" 13#include "lsquic_malo.h" 14#include "lsquic_util.h" 15#include "lsquic_packet_common.h" 16#include "lsquic_packet_out.h" 17#include "lsquic_parse.h" 18#include "lsquic_bw_sampler.h" 19 20#define LSQUIC_LOGGER_MODULE LSQLM_BW_SAMPLER 21#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(sampler->bws_conn) 22#include "lsquic_logger.h" 23 24 25int 26lsquic_bw_sampler_init (struct bw_sampler *sampler, struct lsquic_conn *conn, 27 enum quic_ft_bit retx_frames) 28{ 29 struct malo *malo; 30 31 assert(lsquic_is_zero(sampler, sizeof(*sampler))); 32 33 malo = lsquic_malo_create(sizeof(struct bwp_state)); 34 if (!malo) 35 return -1; 36 37 sampler->bws_malo = malo; 38 sampler->bws_conn = conn; 39 sampler->bws_retx_frames = retx_frames; 40 sampler->bws_flags |= BWS_APP_LIMITED; 41 LSQ_DEBUG("init"); 42 return 0; 43} 44 45 46void 47lsquic_bw_sampler_app_limited (struct bw_sampler *sampler) 48{ 49 sampler->bws_flags |= BWS_APP_LIMITED; 50 sampler->bws_end_of_app_limited_phase = sampler->bws_last_sent_packno; 51 LSQ_DEBUG("app limited, end of limited phase is %"PRIu64, 52 sampler->bws_end_of_app_limited_phase); 53} 54 55 56void 57lsquic_bw_sampler_cleanup (struct bw_sampler *sampler) 58{ 59 if (sampler->bws_conn) 60 LSQ_DEBUG("cleanup"); 61 if (sampler->bws_malo) 62 { 63 lsquic_malo_destroy(sampler->bws_malo); 64 sampler->bws_malo = NULL; 65 } 66} 67 68 69/* This module only fails when it is unable to allocate memory. This rarely 70 * happens, so we avoid having to check return values and abort the connection 71 * instead. 72 */ 73static void 74bw_sampler_abort_conn (struct bw_sampler *sampler) 75{ 76 if (!(sampler->bws_flags & BWS_CONN_ABORTED)) 77 { 78 sampler->bws_flags |= BWS_CONN_ABORTED; 79 LSQ_WARN("aborting connection"); 80 sampler->bws_conn->cn_if->ci_internal_error(sampler->bws_conn, 81 "resources exhausted"); 82 } 83} 84 85 86#define BW_WARN_ONCE(...) do { \ 87 if (!(sampler->bws_flags & BWS_WARNED)) \ 88 { \ 89 sampler->bws_flags |= BWS_WARNED; \ 90 LSQ_WARN(__VA_ARGS__); \ 91 } \ 92} while (0) 93 94void 95lsquic_bw_sampler_packet_sent (struct bw_sampler *sampler, 96 struct lsquic_packet_out *packet_out, uint64_t in_flight) 97{ 98 struct bwp_state *state; 99 unsigned short sent_sz; 100 101 if (packet_out->po_bwp_state) 102 { 103 BW_WARN_ONCE("sent: packet %"PRIu64" already has state", 104 packet_out->po_packno); 105 return; 106 } 107 108 sampler->bws_last_sent_packno = packet_out->po_packno; 109 110 if (!(packet_out->po_frame_types & sampler->bws_retx_frames)) 111 return; 112 113 sent_sz = lsquic_packet_out_sent_sz(sampler->bws_conn, packet_out); 114 sampler->bws_total_sent += sent_sz; 115 116 // If there are no packets in flight, the time at which the new transmission 117 // opens can be treated as the A_0 point for the purpose of bandwidth 118 // sampling. This underestimates bandwidth to some extent, and produces some 119 // artificially low samples for most packets in flight, but it provides with 120 // samples at important points where we would not have them otherwise, most 121 // importantly at the beginning of the connection. 122 if (in_flight == 0) 123 { 124 sampler->bws_last_acked_packet_time = packet_out->po_sent; 125 sampler->bws_last_acked_total_sent = sampler->bws_total_sent; 126 // In this situation ack compression is not a concern, set send rate to 127 // effectively infinite. 128 sampler->bws_last_acked_sent_time = packet_out->po_sent; 129 } 130 131 state = lsquic_malo_get(sampler->bws_malo); 132 if (!state) 133 { 134 bw_sampler_abort_conn(sampler); 135 return; 136 } 137 138 state->bwps_send_state = (struct bwps_send_state) { 139 .total_bytes_sent = sampler->bws_total_sent, 140 .total_bytes_acked = sampler->bws_total_acked, 141 .total_bytes_lost = sampler->bws_total_lost, 142 .is_app_limited = !!(sampler->bws_flags & BWS_APP_LIMITED), 143 }; 144 state->bwps_sent_at_last_ack = sampler->bws_last_acked_total_sent; 145 state->bwps_last_ack_sent_time = sampler->bws_last_acked_sent_time; 146 state->bwps_last_ack_ack_time = sampler->bws_last_acked_packet_time; 147 state->bwps_packet_size = sent_sz; 148 149 packet_out->po_bwp_state = state; 150 151 LSQ_DEBUG("add info for packet %"PRIu64, packet_out->po_packno); 152} 153 154 155void 156lsquic_bw_sampler_packet_lost (struct bw_sampler *sampler, 157 struct lsquic_packet_out *packet_out) 158{ 159 if (!packet_out->po_bwp_state) 160 return; 161 162 sampler->bws_total_lost += packet_out->po_bwp_state->bwps_packet_size; 163 lsquic_malo_put(packet_out->po_bwp_state); 164 packet_out->po_bwp_state = NULL; 165 LSQ_DEBUG("packet %"PRIu64" lost, total_lost goes to %"PRIu64, 166 packet_out->po_packno, sampler->bws_total_lost); 167} 168 169 170struct bw_sample * 171lsquic_bw_sampler_packet_acked (struct bw_sampler *sampler, 172 struct lsquic_packet_out *packet_out, lsquic_time_t ack_time) 173{ 174 const struct bwp_state *state; 175 struct bw_sample *sample; 176 struct bandwidth send_rate, ack_rate; 177 lsquic_time_t rtt; 178 unsigned short sent_sz; 179 int is_app_limited; 180 181 if (!packet_out->po_bwp_state) 182 return 0; 183 184 state = packet_out->po_bwp_state; 185 sent_sz = lsquic_packet_out_sent_sz(sampler->bws_conn, packet_out); 186 187 sampler->bws_total_acked += sent_sz; 188 sampler->bws_last_acked_total_sent = state->bwps_send_state.total_bytes_sent; 189 sampler->bws_last_acked_sent_time = packet_out->po_sent; 190 sampler->bws_last_acked_packet_time = ack_time; 191 192 // Exit app-limited phase once a packet that was sent while the connection 193 // is not app-limited is acknowledged. 194 if ((sampler->bws_flags & BWS_APP_LIMITED) 195 && packet_out->po_packno > sampler->bws_end_of_app_limited_phase) 196 { 197 sampler->bws_flags &= ~BWS_APP_LIMITED; 198 LSQ_DEBUG("exit app-limited phase due to packet %"PRIu64" being acked", 199 packet_out->po_packno); 200 } 201 202 // There might have been no packets acknowledged at the moment when the 203 // current packet was sent. In that case, there is no bandwidth sample to 204 // make. 205 if (state->bwps_last_ack_sent_time == 0) 206 goto no_sample; 207 208 // Infinite rate indicates that the sampler is supposed to discard the 209 // current send rate sample and use only the ack rate. 210 if (packet_out->po_sent > state->bwps_last_ack_sent_time) 211 send_rate = BW_FROM_BYTES_AND_DELTA( 212 state->bwps_send_state.total_bytes_sent 213 - state->bwps_sent_at_last_ack, 214 packet_out->po_sent - state->bwps_last_ack_sent_time); 215 else 216 send_rate = BW_INFINITE(); 217 218 // During the slope calculation, ensure that ack time of the current packet is 219 // always larger than the time of the previous packet, otherwise division by 220 // zero or integer underflow can occur. 221 if (ack_time <= state->bwps_last_ack_ack_time) 222 { 223 BW_WARN_ONCE("Time of the previously acked packet (%"PRIu64") is " 224 "is larger than the ack time of the current packet (%"PRIu64")", 225 state->bwps_last_ack_ack_time, ack_time); 226 goto no_sample; 227 } 228 229 ack_rate = BW_FROM_BYTES_AND_DELTA( 230 sampler->bws_total_acked - state->bwps_send_state.total_bytes_acked, 231 ack_time - state->bwps_last_ack_ack_time); 232 LSQ_DEBUG("send rate: %"PRIu64"; ack rate: %"PRIu64, send_rate.value, 233 ack_rate.value); 234 235 // Note: this sample does not account for delayed acknowledgement time. 236 // This means that the RTT measurements here can be artificially high, 237 // especially on low bandwidth connections. 238 rtt = ack_time - packet_out->po_sent; 239 is_app_limited = state->bwps_send_state.is_app_limited; 240 241 /* After this point, we switch `sample' to point to `state' and don't 242 * reference `state' anymore. 243 */ 244 sample = (void *) packet_out->po_bwp_state; 245 packet_out->po_bwp_state = NULL; 246 if (BW_VALUE(&send_rate) < BW_VALUE(&ack_rate)) 247 sample->bandwidth = send_rate; 248 else 249 sample->bandwidth = ack_rate; 250 sample->rtt = rtt; 251 sample->is_app_limited = is_app_limited; 252 253 LSQ_DEBUG("packet %"PRIu64" acked, bandwidth: %"PRIu64" bps", 254 packet_out->po_packno, BW_VALUE(&sample->bandwidth)); 255 256 return sample; 257 258 no_sample: 259 lsquic_malo_put(packet_out->po_bwp_state); 260 packet_out->po_bwp_state = NULL; 261 return NULL;; 262} 263 264 265unsigned 266lsquic_bw_sampler_entry_count (const struct bw_sampler *sampler) 267{ 268 void *el; 269 unsigned count; 270 271 count = 0; 272 for (el = lsquic_malo_first(sampler->bws_malo); el; 273 el = lsquic_malo_next(sampler->bws_malo)) 274 ++count; 275 276 return count; 277} 278