Packages
hackney
3.0.2
4.7.2
4.7.1
4.7.0
4.6.1
4.6.0
4.5.2
4.5.1
4.5.0
4.4.5
4.4.3
4.4.2
4.4.1
4.4.0
4.3.0
4.2.3
4.2.2
4.2.1
4.2.0
4.1.0
4.0.3
4.0.2
4.0.1
4.0.0
3.2.1
3.2.0
3.1.2
3.1.1
3.1.0
3.0.3
3.0.2
3.0.1
3.0.0
retired
2.0.1
2.0.0
2.0.0-beta.1
1.25.0
1.24.1
1.24.0
1.23.0
1.22.0
1.21.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.2
1.18.1
1.18.0
1.17.4
1.17.3
1.17.2
1.17.1
1.17.0
1.16.0
1.15.2
1.15.1
1.15.0
1.14.3
1.14.2
1.14.0
1.13.0
1.12.1
1.12.0
1.11.0
1.10.1
1.10.0
1.9.0
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.0
1.7.1
1.7.0
1.6.6
retired
1.6.5
1.6.4
retired
1.6.3
1.6.2
1.6.1
1.6.0
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.10
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.0
1.0.6
1.0.5
1.0.2
1.0.1
0.15.2
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.1
Simple HTTP client with HTTP/1.1, HTTP/2, and HTTP/3 support
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
c_src/lsquic/src/liblsquic/lsquic_bw_sampler.c
/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/queue.h>
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_hash.h"
#include "lsquic.h"
#include "lsquic_conn.h"
#include "lsquic_malo.h"
#include "lsquic_util.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_bw_sampler.h"
#define LSQUIC_LOGGER_MODULE LSQLM_BW_SAMPLER
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(sampler->bws_conn)
#include "lsquic_logger.h"
int
lsquic_bw_sampler_init (struct bw_sampler *sampler, struct lsquic_conn *conn,
enum quic_ft_bit retx_frames)
{
struct malo *malo;
assert(lsquic_is_zero(sampler, sizeof(*sampler)));
malo = lsquic_malo_create(sizeof(struct bwp_state));
if (!malo)
return -1;
sampler->bws_malo = malo;
sampler->bws_conn = conn;
sampler->bws_retx_frames = retx_frames;
sampler->bws_flags |= BWS_APP_LIMITED;
LSQ_DEBUG("init");
return 0;
}
void
lsquic_bw_sampler_app_limited (struct bw_sampler *sampler)
{
sampler->bws_flags |= BWS_APP_LIMITED;
sampler->bws_end_of_app_limited_phase = sampler->bws_last_sent_packno;
LSQ_DEBUG("app limited, end of limited phase is %"PRIu64,
sampler->bws_end_of_app_limited_phase);
}
void
lsquic_bw_sampler_cleanup (struct bw_sampler *sampler)
{
if (sampler->bws_conn)
LSQ_DEBUG("cleanup");
if (sampler->bws_malo)
{
lsquic_malo_destroy(sampler->bws_malo);
sampler->bws_malo = NULL;
}
}
/* This module only fails when it is unable to allocate memory. This rarely
* happens, so we avoid having to check return values and abort the connection
* instead.
*/
static void
bw_sampler_abort_conn (struct bw_sampler *sampler)
{
if (!(sampler->bws_flags & BWS_CONN_ABORTED))
{
sampler->bws_flags |= BWS_CONN_ABORTED;
LSQ_WARN("aborting connection");
sampler->bws_conn->cn_if->ci_internal_error(sampler->bws_conn,
"resources exhausted");
}
}
#define BW_WARN_ONCE(...) do { \
if (!(sampler->bws_flags & BWS_WARNED)) \
{ \
sampler->bws_flags |= BWS_WARNED; \
LSQ_WARN(__VA_ARGS__); \
} \
} while (0)
void
lsquic_bw_sampler_packet_sent (struct bw_sampler *sampler,
struct lsquic_packet_out *packet_out, uint64_t in_flight)
{
struct bwp_state *state;
unsigned short sent_sz;
if (packet_out->po_bwp_state)
{
BW_WARN_ONCE("sent: packet %"PRIu64" already has state",
packet_out->po_packno);
return;
}
sampler->bws_last_sent_packno = packet_out->po_packno;
if (!(packet_out->po_frame_types & sampler->bws_retx_frames))
return;
sent_sz = lsquic_packet_out_sent_sz(sampler->bws_conn, packet_out);
sampler->bws_total_sent += sent_sz;
// If there are no packets in flight, the time at which the new transmission
// opens can be treated as the A_0 point for the purpose of bandwidth
// sampling. This underestimates bandwidth to some extent, and produces some
// artificially low samples for most packets in flight, but it provides with
// samples at important points where we would not have them otherwise, most
// importantly at the beginning of the connection.
if (in_flight == 0)
{
sampler->bws_last_acked_packet_time = packet_out->po_sent;
sampler->bws_last_acked_total_sent = sampler->bws_total_sent;
// In this situation ack compression is not a concern, set send rate to
// effectively infinite.
sampler->bws_last_acked_sent_time = packet_out->po_sent;
}
state = lsquic_malo_get(sampler->bws_malo);
if (!state)
{
bw_sampler_abort_conn(sampler);
return;
}
state->bwps_send_state = (struct bwps_send_state) {
.total_bytes_sent = sampler->bws_total_sent,
.total_bytes_acked = sampler->bws_total_acked,
.total_bytes_lost = sampler->bws_total_lost,
.is_app_limited = !!(sampler->bws_flags & BWS_APP_LIMITED),
};
state->bwps_sent_at_last_ack = sampler->bws_last_acked_total_sent;
state->bwps_last_ack_sent_time = sampler->bws_last_acked_sent_time;
state->bwps_last_ack_ack_time = sampler->bws_last_acked_packet_time;
state->bwps_packet_size = sent_sz;
packet_out->po_bwp_state = state;
LSQ_DEBUG("add info for packet %"PRIu64, packet_out->po_packno);
}
void
lsquic_bw_sampler_packet_lost (struct bw_sampler *sampler,
struct lsquic_packet_out *packet_out)
{
if (!packet_out->po_bwp_state)
return;
sampler->bws_total_lost += packet_out->po_bwp_state->bwps_packet_size;
lsquic_malo_put(packet_out->po_bwp_state);
packet_out->po_bwp_state = NULL;
LSQ_DEBUG("packet %"PRIu64" lost, total_lost goes to %"PRIu64,
packet_out->po_packno, sampler->bws_total_lost);
}
struct bw_sample *
lsquic_bw_sampler_packet_acked (struct bw_sampler *sampler,
struct lsquic_packet_out *packet_out, lsquic_time_t ack_time)
{
const struct bwp_state *state;
struct bw_sample *sample;
struct bandwidth send_rate, ack_rate;
lsquic_time_t rtt;
unsigned short sent_sz;
int is_app_limited;
if (!packet_out->po_bwp_state)
return 0;
state = packet_out->po_bwp_state;
sent_sz = lsquic_packet_out_sent_sz(sampler->bws_conn, packet_out);
sampler->bws_total_acked += sent_sz;
sampler->bws_last_acked_total_sent = state->bwps_send_state.total_bytes_sent;
sampler->bws_last_acked_sent_time = packet_out->po_sent;
sampler->bws_last_acked_packet_time = ack_time;
// Exit app-limited phase once a packet that was sent while the connection
// is not app-limited is acknowledged.
if ((sampler->bws_flags & BWS_APP_LIMITED)
&& packet_out->po_packno > sampler->bws_end_of_app_limited_phase)
{
sampler->bws_flags &= ~BWS_APP_LIMITED;
LSQ_DEBUG("exit app-limited phase due to packet %"PRIu64" being acked",
packet_out->po_packno);
}
// There might have been no packets acknowledged at the moment when the
// current packet was sent. In that case, there is no bandwidth sample to
// make.
if (state->bwps_last_ack_sent_time == 0)
goto no_sample;
// Infinite rate indicates that the sampler is supposed to discard the
// current send rate sample and use only the ack rate.
if (packet_out->po_sent > state->bwps_last_ack_sent_time)
send_rate = BW_FROM_BYTES_AND_DELTA(
state->bwps_send_state.total_bytes_sent
- state->bwps_sent_at_last_ack,
packet_out->po_sent - state->bwps_last_ack_sent_time);
else
send_rate = BW_INFINITE();
// During the slope calculation, ensure that ack time of the current packet is
// always larger than the time of the previous packet, otherwise division by
// zero or integer underflow can occur.
if (ack_time <= state->bwps_last_ack_ack_time)
{
BW_WARN_ONCE("Time of the previously acked packet (%"PRIu64") is "
"is larger than the ack time of the current packet (%"PRIu64")",
state->bwps_last_ack_ack_time, ack_time);
goto no_sample;
}
ack_rate = BW_FROM_BYTES_AND_DELTA(
sampler->bws_total_acked - state->bwps_send_state.total_bytes_acked,
ack_time - state->bwps_last_ack_ack_time);
LSQ_DEBUG("send rate: %"PRIu64"; ack rate: %"PRIu64, send_rate.value,
ack_rate.value);
// Note: this sample does not account for delayed acknowledgement time.
// This means that the RTT measurements here can be artificially high,
// especially on low bandwidth connections.
rtt = ack_time - packet_out->po_sent;
is_app_limited = state->bwps_send_state.is_app_limited;
/* After this point, we switch `sample' to point to `state' and don't
* reference `state' anymore.
*/
sample = (void *) packet_out->po_bwp_state;
packet_out->po_bwp_state = NULL;
if (BW_VALUE(&send_rate) < BW_VALUE(&ack_rate))
sample->bandwidth = send_rate;
else
sample->bandwidth = ack_rate;
sample->rtt = rtt;
sample->is_app_limited = is_app_limited;
LSQ_DEBUG("packet %"PRIu64" acked, bandwidth: %"PRIu64" bps",
packet_out->po_packno, BW_VALUE(&sample->bandwidth));
return sample;
no_sample:
lsquic_malo_put(packet_out->po_bwp_state);
packet_out->po_bwp_state = NULL;
return NULL;;
}
unsigned
lsquic_bw_sampler_entry_count (const struct bw_sampler *sampler)
{
void *el;
unsigned count;
count = 0;
for (el = lsquic_malo_first(sampler->bws_malo); el;
el = lsquic_malo_next(sampler->bws_malo))
++count;
return count;
}