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